Introduction

Figure caption: Main causes of deforestation in central Menabe. a-a’: Slash-and-burn agriculture (“hatsake”) for peanut crop. Peanut (a’) is cultivated as a cash crop. Part of the production is at the destination of the national market but most of it is exported outside Madagascar, mainly for the Chinese market. b-b’: Slash-and-burn agriculture for maize crop. maize (b’) is cultivated for auto-consumption and as a cash crop. The production of maize is at the destination of the national market and is used in particular to brew the national beers. c-c’: Cyclone followed by uncontrolled fires. Cyclone “Fanele” (2009) caused tree mortality and accumulation of wood fuel on the ground. As a consequence, uncontrolled fires set on nearby pastures (c’) spread over large areas of forest after 2009. d-d’: Illegal logging. Timbers are used for house and boat construction.

This tutorial is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.

Computing environment

Installing Docker

Docker is used to run software packages called “containers”. Containers are isolated from each other and bundle their own tools, libraries and configuration files. Containers are created from “images” that specify their precise contents.

To install Docker for your operating system, go to https://docs.docker.com/install/. For Windows user, go directly to https://docs.docker.com/docker-for-windows/install/.

Docker image

For our tutorial, we have built a specific Docker image including R and Python:

  • R computing environment: R 3.5.1, RStudio, and various R packages (ex. knitr, ggplot2, reticulate).
  • Python computing environment: Python 3, and various Python packages (ex. numpy, pandas, matplotlib, and forestatrisk).

Some computations, such as computation by blocks on large raster files, were more easily coded and faster with Python. That’s why we decided to use Python as a complementary language to R. The reticulate R package allow us to use Python functions from R and thus to work in a single computing environment within RStudio.

Load the forestatrisk Docker image

Download the image from internet with the following command in a terminal: docker pull ghislainv/docker-forestatrisk . (do not forget the final “.”)

Note: If the internet connection is slow, use a USB key and copy the image somewhere on your computer and load it with the following command: docker load -i <path to image tar file>

Run the container with docker run -d -e PASSWORD=PWD -e ROOT=YES -p 8787:8787 -v ORIGIN_FOLDER:/home/rstudio ghislainv/docker-forestatrisk.

Note:

  • Set your own password PWD, it cannot be rstudio.
  • Replace ORIGIN_FOLDER with a folder on your local machine where you want to save the R scripts. For example /c/Users/YOUR_NAME or /home/YOUR_NAME.

Point your browser to localhost:8787 to access RStudio interface. Log in with rstudio/PWD.

Get the tutorial

In RStudio, open a terminal and clone the GitHub repository locally:
git clone https://github.com/ghislainv/forecasting-deforestation-Mada

Navigate with RStudio in the folder named forecasting-deforestation-Mada and open the R Notebook file named training.Rmd. R Markdown files (*.Rmd) can be used in R to embbed text, code, table and figues inside a unique document. Code are organized into ‘chunks’ that can be executed independently and interactively. An R Notebook is an R Markdown document with output visible immediately beneath the input.

I used this notebook to write the tutorial available at https://ecology.ghislainv.fr/forecasting-deforestation-Mada.

In the preambule, change the author and date with your name and today’s date.

Importing Python modules in R

Specify the Python version to use and check that it is the right one.

use_python("/usr/bin/python3.5")
py_config()
python:         /usr/bin/python3.5
libpython:      /usr/lib/python3.5/config-3.5m-x86_64-linux-gnu/libpython3.5.so
pythonhome:     /usr:/usr
version:        3.5.3 (default, Sep 27 2018, 17:25:39)  [GCC 6.3.0 20170516]
numpy:          /usr/local/lib/python3.5/dist-packages/numpy
numpy_version:  1.15.3

python versions found: 
 /usr/bin/python3.5
 /usr/bin/python
 /usr/bin/python3

Import the Python modules into R.

far <- import("forestatrisk")
patsy <- import("patsy")
sm <- import("statsmodels.api")
smf <- import("statsmodels.formula.api")

R/Python intro

R

Some exercises
a = c(20,5,6,2,9,12)
b = 1
c = a + b
print(c)
[1] 21  6  7  3 10 13
Simple regression
# Generating data
nobs <- 100
x.seq <- runif(n=nobs,min=0,max=100)
a.target <- 2
b.target <- 2
sigma2.target <- 300
y.seq <- a.target + b.target*x.seq +
         rnorm(n=nobs,mean=0,sd=sqrt(sigma2.target))
# Data-set
Data <- as.data.frame(cbind(y.seq,x.seq))
# Plot
plot(x.seq,y.seq)

# Estimation
M <- lm(y.seq~x.seq,data=Data)
summary(M)

Call:
lm(formula = y.seq ~ x.seq, data = Data)

Residuals:
    Min      1Q  Median      3Q     Max 
-43.052 -13.131  -1.399  12.603  48.548 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  6.93329    3.78986   1.829   0.0704 .  
x.seq        1.96978    0.06754  29.167   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 18.91 on 98 degrees of freedom
Multiple R-squared:  0.8967,    Adjusted R-squared:  0.8956 
F-statistic: 850.7 on 1 and 98 DF,  p-value: < 2.2e-16
M$coefficients
(Intercept)       x.seq 
   6.933290    1.969783 
var(M$residuals)
[1] 354.0755
M$df
[1] 98
# Graphics 
x.pred <- seq(from=0,to=100,length.out=100) # We will predict y for 100 values of x
X.pred <- as.data.frame(x.pred)
names(X.pred) <- "x.seq"
Y.pred <- predict.lm(M,X.pred,interval="confidence",
                     type="response")
plot(x.seq,y.seq,col=grey(0.7),
     xlab="X",
     ylab="Y",
     main="Simple linear regression") # Row data
# Confidence envelops for predictive posterior
lines(x.pred,Y.pred[,1],col="red",lwd=3)
lines(x.pred,Y.pred[,2],col="blue",lwd=2,lty=2)
lines(x.pred,Y.pred[,3],col="blue",lwd=2,lty=2)

Python

Some exercises

You can execute Python code within a R notebooks.

import numpy as np
a = np.array([20,5,6,2,9,12])
b = 1
c = a + b
print(type(c))
print(c)
<class 'numpy.ndarray'>
[21  6  7  3 10 13]

Preparing the data-set

Downloading the geospatial data

To model the spatial probability of deforestation, we need a map of the past deforestation and maps of potential explicative environmental variables. Environmental variables can be derived from topography (altitude and slope), accessibility (distance to roads, towns, and forest edge), deforestation history (distance to previous deforestation) or landscape policy (protected area network) for example. In our example, we use the following variables :

library(kableExtra)
tab1 <- read.table("training/tables/variables.txt",sep=",",header=TRUE)
names(tab1) <- c("Product","Source","Variable derived","Unit","Resolution (m)")
# Kable
knitr::kable(tab1, linesep="") %>%
  kable_styling(bootstrap_options = c("striped"))
Product Source Variable derived Unit Resolution (m)
Deforestation maps (1990-2000-2010) BioSceneMada (1) forest/non-forest -- 30
distance to forest edge m 30
distance to previous deforestation m 30
Digital Elevation Model SRTM v4.1 CSI-CGIAR (2) altitude m 90
slope ° 90
Highways OSM - Geofabrik (3) distance to roads m 150
Places distance to towns m 150
Waterways distance to river m 150
Protected areas Rebioma (4) presence of protected area -- 30
  1. http://bioscenemada.cirad.fr, deforestation maps from Vieilledent et al. (2018)
  2. http://srtm.csi.cgiar.org, SRTM 90m Digital Elevation Database v4.1
  3. http://www.geofabrik.de, data extracts from the OpenStreetMap project for Madagascar,
  4. http://rebioma.net, SAPM (“Système des Aires Protégées à Madagascar”), 20/12/2010 version
# Make data directory
if (!dir.exists("data")) {
  dir.create("data")
  dir.create("data/model")
  dir.create("data/mada")
  dir.create("data/validation")
}
# Files
files <- c("fordefor2010.tif", "dist_edge.tif", "dist_defor.tif",
           "altitude.tif", "slope.tif", "aspect.tif",
           "dist_road.tif", "dist_town.tif", "dist_river.tif",
           "sapm.tif", "roads.kml", "towns.kml", "rivers.kml", "sapm.kml")
# Download model data
for (i in files) {
  if (!file.exists(glue("data/model/{i}"))) {
    f <- glue("https://zenodo.org/record/259582/files/{i}")
    curl_download(f, glue("data/model/{i}"), quiet=FALSE)
  }
}
# Download validation data
if (!file.exists("data/validation/for2017.tif")) {
  f <- "http://bioscenemada.cirad.fr/FileTransfer/for2017.tif"
  curl_download(f, "data/validation/for2017.tif", quiet=FALSE)
}
# Download mada outline
if (!file.exists("data/mada/mada38s.shp")) {
  f <- "http://bioscenemada.cirad.fr/FileTransfer/mada38s.zip"
  curl_download(f, "data/mada/mada38s.zip", quiet=FALSE)
  unzip("data/mada/mada38s.zip", exdir="data/mada")
}

In our example, fordefor2010.tif is a forest raster at 30m for the year 2010 considering the deforestation on the period 2000-2010 in Madagascar. We can plot this raster and zoom on a region with function .plot.forest() in the forestatrisk package to see the deforestation data. The remaining forest appears in green and the deforested areas appear in red.

# Plot forest cover change 2000-2010
fig <- far$plot$fcc(input_fcc_raster="data/model/fordefor2010.tif",
             output_file="output/fcc.png",
             col=c(255,0,0,255),  # rgba color for deforestation
             figsize=c(5,5),
             dpi=150,
             zoom=c(340000,412000,7420000,7500000))
knitr::include_graphics("output/fcc.png")

Sampling points

We use the function .sample() from the forestatrisk module to sample 10,000 points (pixel centers) in the deforested areas and in the remaining forest (20,000 points in total). The input_forest_raster argument defines the path to the forest raster including the deforested pixels (with value=0) and the remaining forest pixels (value=1) after a given period of time. The random seed can be set with argument Seed to reproduce the data of the random sampling.

The .sample() function also extracts information from environmental variables for each sampled point. Sampling is done by block to allow the computation on large study areas (e.g. country or continental scale) with a fine spatial resolution (e.g. 30m). The var_dir argument indicates the directory including all the environmental raster files (they must be GeoTIFF files with extension .tif) that we want to test.

The .sample() function identifies the spatial cell for each sample point (sample point are grouped within a spatial cell). Spatial cells and grouped observations are used to estimate the spatial autocorrelation of the deforestation process. The csize argument define the width (in km) of the square spatial cells. Each spatial cell will be attributed a parameter. To avoid estimating too many parameters, width of the square spatial cells must be sufficiently large. Both points sampling and extraction of environmental data are done by block to avoid memory problems for big datasets.

# Make output directory
if (!dir.exists("output")) {
  dir.create("output")
}
# Training data-set
if (!file.exists("output/sample.txt")) {
  samp <- far$sample(nsamp=10000L, Seed=1234L, csize=10L,
                     var_dir="data/model",
                     input_forest_raster="fordefor2010.tif",
                     output_file="output/sample.txt",
                     blk_rows=1L)
}
samp <- read.table("output/sample.txt", header=TRUE, sep=",")
set.seed(1234)
train <- sample(1:20000, size=10000, replace=FALSE)
data_train <- samp[train,] %>% dplyr::filter(complete.cases(.))
data_valid <- samp[-train,] %>% dplyr::filter(complete.cases(.))
head(data_train)

Sampled observations can be plotted using function .plot.obs() from the deforestprob module. Dark red dots indicate deforestation observations and dark green dots indicate forest observations.

# Plot sample points
fig <- far$plot$obs(sample=data_train,
             name_forest_var="fordefor2010",
             input_fcc_raster="data/model/fordefor2010.tif",
             output_file="output/obs.png",
             zoom=c(340000,412000,7420000,7500000),
             figsize=c(5,5),#c(11.69,8.27),
             s=5,dpi=300)
knitr::include_graphics("output/obs.png")

Descriptive statistics

Before modelling the deforestation, it is important to look at the relationship between environmental variables and deforestation. Using formulas from the patsy Python module, we can specify the relationships that we want to look at. In the example below, we plot the relationships between some continuous environmental variables and the probability of deforestation using function .plot.correlation() from the forestatrisk package. Note that -1 must be set at the end of the formula. The function .correlation() returns a serie of graphics that can be analyzed to choose the right relationship for each continuous variable (linear or polynomial for example).

# Descriptive statistics
# Model formulas
formula_1 <- paste0("fordefor2010 ~ dist_road + dist_town + dist_defor +",
                    "dist_river + dist_edge + altitude + slope + aspect - 1")
# Standardized variables (mean=0, std=1)
formula_2 <- paste0("fordefor2010 ~ scale(dist_road) + scale(dist_town) +",
                    "scale(dist_defor) + scale(dist_river) + scale(dist_edge) +",
                    "scale(altitude) + scale(slope) + scale(aspect) - 1")
formulas <- c(formula_1, formula_2)
# List to store figures
corr_fig <- list()
# Loop on formulas
for (f in 1:length(formulas)) {
    # Output file
    of <- glue("output/correlation_{f}.pdf")
    # Data
    dmat <- patsy$dmatrices(formulas[f], data=data_train, eval_env=-1L,
                                  return_type="dataframe")
    # Plots
    fig <- far$plot$correlation(y=dmat[[1]],data=dmat[[2]],
                         plots_per_page=3L,figsize=c(7,8),
                         dpi=100L,output_file=of)
}

In this example (see pdf files produced), we can see that a linear model should be sufficient to represent the relationship between the probability of deforestation and the standardized distance to the nearest road or town. On the contrary, it could be interesting to fit a polynomial model for the the standardized distance to previous deforestation (dist_defor variable) for which the relationship seems non-linear. Several models can be fitted and compared to see if a second-order or third-order polynomial relationship is justified.

Deforestation model

We propose to use the Binomial iCAR model (Vieilledent et al. 2014) to estimate the deforestation probability of a pixel given a set of environmental variables. The Binomial iCAR model is a linear Binomial logistic regression model including an intrinsic Conditional Autoregressive (iCAR) process to account for the spatial autocorrelation of the observations. Parameter inference is done in a hierarchical Bayesian framework. The .model_binomial_iCAR() function from the forestatrisk module includes a Metropolis-within-Gibbs algorithm written in pure C code to reduce computation time.

Figure caption: Parameter inference is done in a hierarchical Bayesian framework. Bayesian statistics rely on the Bayes’ theorem named after Reverend Thomas Bayes. Each parameter has a prior and an approximated posterior probability distribution from which we can compute the mean, standard deviation, credible intervals at 95%, etc.

For the deforestation process it is very important to take into account the spatial autocorrelation of the process with spatial random effects. Indeed, the selected fixed environmental variables are not able to fully explain the spatial variability of the deforestation process, especially when working at large geographical scales, such as the national or continental scale. Spatial random effects allow estimating a higher/lower probability of deforestation in a particular region (associated to unmeasurable or unknow factors) that is different from the mean probability of deforestation derived from the environmental factors included in the model. The Binomial iCAR model can be described as follow:

Ecological process

\[\begin{equation} y_i \sim \mathcal{B}inomial(\theta_i,t_i) \\ \text{logit}(\theta_i) = X_i \beta + \rho_{j(i)} \end{equation}\]

\(y_i\): random variable for the deforestation process (0 if no deforestation, 1 if deforestation)

\(\theta_i\): probability of deforestation

\(t_i\): number of trials (always 1 in our example)

\(X_i\): vector of values for environmental explicative variables

\(\beta\): vector of fixed effect parameters

\(\rho_j\): spatial random effect

\(j(i)\): index of the spatial entity for observation \(i\).

Spatial autocorrelation

The spatial autocorrelation is modelled with an intrinsic conditional autoregressive (iCAR) process:

\[\begin{equation} \rho_j \sim \mathcal{N}ormal(\mu_j,V_{\rho} / n_j) \end{equation}\]

\(\mu_j\): mean of \(\rho_{j'}\) in the neighborhood of \(j\).

\(V_{\rho}\): variance of the spatial random effects.

\(n_j\): number of neighbors for spatial entity \(j\).

Figure caption: Representation of the neighborhood for the intrinsic conditional autoregressive (iCAR) process. Target spatial cell \(j\) has 8 neighbors in this case. Several observations (black points, equivalent to pixel centers in our case) can be located in each spatial cell. Deforestation probability in one spatial cell \(j\) depends on deforestation probability in neighboring cells.

Spatial cells

Before running the model, we add a column indicating the number of trials for each observation (1 in our case as we are considering a Bernoulli process). We then remove any observation with non-available data (NA) from the data-set. We also compute the number of neighbors (nneigh) and the neighbor identifiers (adj) for each spatial cell using function .cellneigh from the forestatrisk module.

# Spatial cells for spatial-autocorrelation
neighborhood <- far$cellneigh_ctry(raster="data/model/fordefor2010.tif",
                                   vector="data/mada/mada38s.shp",
                                   csize=10L, rank=1L)
nneigh <- neighborhood[[1]]
adj <- neighborhood[[2]]
cell_in <- neighborhood[[3]]
ncell <- neighborhood[[4]]
# Udpate cell number in dataset
cell_rank <- vector()
for (i in 1:nrow(data_train)) {
  cell_rank[i] <- which(cell_in==data_train$cell[i])-1 # ! cells start at zero
}
data_train$cell <- cell_rank

Model formula

A model formula must also be defined to specify the explicative variables we want to include in the model. The formula allows specifying some variable transformations (such as standardization in our case). See the patsy module for more information. In our model, we included the following variables: location inside a protected area, altitude, distance to past deforestation (with a degree two polynomial), distance to forest edge, distance to nearest road and distance to nearest town. The formula must end with the name of the variable indicating the spatial cell for each observation (cell in our case).

# Formula
data_train$trials <- 1  # Set number of trials to one
formula <- paste0("I(1-fordefor2010) + trials ~ C(sapm) + scale(altitude) + scale(slope) +",
                  "scale(dist_defor) + np.power(scale(dist_defor),2) + ",
                  "scale(dist_edge) + ",
                  "scale(dist_road) + scale(dist_town) + cell")

Fitting model parameters

# Model
mod_binomial_iCAR <- far$model_binomial_iCAR(
  # Observations
  suitability_formula=formula, data=data_train,
  # Spatial structure
  n_neighbors=np_array(nneigh,dtype="int32"), neighbors=np_array(adj,dtype="int32"),
  # Environment
  eval_env=-1L,
  # Chains
  burnin=1000L, mcmc=1000L, thin=1L,
  # Starting values
  beta_start=-99)

Running the Gibbs sampler. It may be long, please keep cool :)

**********:10.0%, mean accept. rates= beta:0.061, rho:0.525
**********:20.0%, mean accept. rates= beta:0.153, rho:0.408
**********:30.0%, mean accept. rates= beta:0.199, rho:0.312
**********:40.0%, mean accept. rates= beta:0.241, rho:0.288
**********:50.0%, mean accept. rates= beta:0.233, rho:0.258
**********:60.0%, mean accept. rates= beta:0.269, rho:0.257
**********:70.0%, mean accept. rates= beta:0.280, rho:0.258
**********:80.0%, mean accept. rates= beta:0.257, rho:0.261
**********:90.0%, mean accept. rates= beta:0.286, rho:0.252
**********:100.0%, mean accept. rates= beta:0.283, rho:0.247

Model summary

Once the model has been fitted, we can print a summary of the model showing the parameter estimates. The 95% credible intervals obtained from the posterior distribution of each parameter, except distance to nearest town (dist_town), do not include zero, indicating that parameters are significantly different from zero. The variance of the spatial random effects (Vrho) is given together with the deviance value, which can be used to compare different statistical models (lower deviance is better). Looking at the parameter estimates, we can see that the deforestation probability is much lower inside protected areas and that deforestation probability decreases with altitude, slope, distance to past deforestation, forest edge, roads and towns. Parameter values are then coherent regarding the deforestation process and easy to interpret.

sink(file="output/summary_mod_binomial_iCAR.txt")
print(mod_binomial_iCAR)
sink()
print(mod_binomial_iCAR)
Binomial logistic regression with iCAR process
  Model: I(1 - fordefor2010) + trials ~ 1 + C(sapm) + scale(altitude) + scale(slope) + scale(dist_defor) + np.power(scale(dist_defor), 2) + scale(dist_edge) + scale(dist_road) + scale(dist_town) + cell
  Posteriors:
                                     Mean        Std     CI_low    CI_high
                     Intercept     -0.264     0.0833      -0.44    -0.0874
                C(sapm)[T.1.0]     -0.541        0.1     -0.742     -0.369
               scale(altitude)     -0.745     0.0663     -0.892      -0.61
                  scale(slope)     -0.161     0.0442     -0.255    -0.0811
             scale(dist_defor)     -0.863     0.0576     -0.974     -0.759
np.power(scale(dist_defor), 2)     0.0666    0.00813     0.0501     0.0817
              scale(dist_edge)     -0.552     0.0577     -0.662     -0.452
              scale(dist_road)      -0.19     0.0499     -0.285     -0.103
              scale(dist_town)     0.0327     0.0481    -0.0549      0.126
                          Vrho       8.62      0.591       7.54       9.68
                      Deviance   9.51e+03       60.9    9.4e+03   9.63e+03

Plot traces and posteriors

To check for the convergence of the Markov chain Monte Carlo (MCMC), we can plot the traces and the posterior distributions of the estimated parameters using method .plot() associated to the hSDM_binomial_iCAR class defined in the deforestprob module. This method returns the figures showing the traces and posterior distributions.

require(coda)
mcmc <- as.mcmc(mod_binomial_iCAR$mcmc)
plot(mcmc[,c(1:3,10,11)])

Forecasting deforestation

Resampling the spatial random effects

We use the model to predict the spatial probability of deforestation at the national scale for Madagascar. Before, doing so, we smooth the spatial random effects which have been estimated at a coarse resolution (10km in our example). To do so, we use the function .resample_rho() from the deforestprob module to resample the results at a finer resolution using a bilinear interpolation. The function writes a raster file to the disk with a resolution of the raster specified in the argument input_raster of the function (1km in our case). The function .resample_rho() returns a figure of the spatial random effects that can be plotted.

# Get the spatial random effects
rho <- rep(-9999,ncell)  # -9999 will be considered as nodata
rho[cell_in+1] <- mod_binomial_iCAR$rho
# Resample them
fig <- far$resample_rho(rho=r_to_py(rho), input_raster="data/model/fordefor2010.tif",
                 output_file="output/rho.tif",
                 csize_orig=10L, csize_new=1L)
# Plot random effects
fig <- far$plot$rho("output/rho_orig.tif",output_file="output/rho_orig.png")
fig <- far$plot$rho("output/rho.tif",output_file="output/rho.png")
# Plot with R
mada <- rgdal::readOGR(dsn="data/mada",layer="mada38s", verbose=FALSE)
r.rho_orig <- raster("output/rho_orig.tif")
r.rho <- raster("output/rho.tif")
rho_plot(r.rho_orig, mada, output_file="output/rho_orig_ggplot.png",
         quantiles_legend=c(0.025,0.975),width=4.5, height=8)
Results plotted to "output/rho_orig_ggplot.png"

rho_plot(r.rho, mada, output_file="output/rho_ggplot.png",
         quantiles_legend=c(0.025,0.975),width=4.5, height=8)
Results plotted to "output/rho_ggplot.png"

Computing spatial probability of deforestation

The .predict_raster_binomial_iCAR() function of the forestatrisk module can be used to predict the spatial probability of deforestation from an hSDM_binomial_iCAR model (i.e. an object of class hSDM_binomial_iCAR). The function writes a raster of predictions to the disk. The prediction is done by block to avoid memory problems for big datasets. Functions will return NA for pixels with no forest or for pixels with missing environmental variables.

far$predict_raster_binomial_iCAR(mod_binomial_iCAR, var_dir="data/model",
                                 input_cell_raster="output/rho.tif",
                                 input_forest_raster="data/model/fordefor2010.tif",
                                 output_file="output/pred_binomial_iCAR.tif",
                                 blk_rows=128L)

The raster of predictions can then be plotted.

fig <- far$plot$prob("output/pred_binomial_iCAR.tif",
                     output_file="output/pred_binomial_iCAR.png",
                     figsize=c(4,4))
knitr::include_graphics("output/pred_binomial_iCAR.png")

Predicting future forest cover

Given the spatial probability of deforestation and the number of hectares that should be deforested, we can predict the future forest cover using function .deforest() from the forestatrisk package. The number of hectares are converted into number of pixels to be deforested. Pixels with the highest probability of deforestation are deforested first. The function computes a probability threshold above which pixels are deforested.

In our example, we consider an annual deforestation of roughly 100,000 ha for Madagascar. Considering the period 2010-2050, this would correspond to 4 Mha of deforestation. This number can of course be more precise and refined considering various deforestation scenarios (demographic growth, economic development, etc.).

deforest <- far$deforest(input_raster="output/pred_binomial_iCAR.tif",
                         hectares=4000000,
                         output_file="output/forest_cover_2050.tif",
                         blk_rows=128L)

We can plot the predicted future forest cover in 2050 with leaflet. We first reproject the raster to WGS 84 / World Mercator projection (EPSG:3857) and we resample the map at 250 m using function gdalwarp from GDAL to obtain a lower resolution raster that can be transformed into a lower size image that can be plotted with leaflet.

gdalwarp -overwrite -s_srs EPSG:32738 -t_srs EPSG:3857 -tr 250 250 \
         -ot Byte -r near -co "COMPRESS=LZW" -co "PREDICTOR=2" -co "BIGTIFF=YES" \
         output/forest_cover_2050.tif output/forest_cover_2050_epsg3857_ov32.tif
Creating output file that is 3556P x 6534L.
Processing input file output/forest_cover_2050.tif.
Using internal nodata values (e.g. 255) for image output/forest_cover_2050.tif.
Copying nodata values from source output/forest_cover_2050.tif to destination output/forest_cover_2050_epsg3857_ov32.tif.
0...10...20...30...40...50...60...70...80...90...100 - done.

We also set the color of the map and the extent of the view for leaflet.

# Colors and extent view for leaflet
r <- raster("output/forest_cover_2050_epsg3857_ov32.tif")
pal <- colorFactor(c("red", "darkgreen"), c(0,1), na.color = "transparent")
r1 <- raster(nrows=1,ncols=1,ext=extent(340000,412000,7420000,7500000),crs=CRS("+init=epsg:32738"))
r2 <- projectExtent(r1, crs=CRS("+init=epsg:4326"))
ext2 <- extent(r2)

The red areas represent the deforestation on the period 2010-2050. The green areas represent the remaining forest in 2050. Most of the remaining forest in 2050 are inside the protected areas or located in remote areas, at high altitudes and far from roads and big cities (for example in the Tsaratanana mountain region and around the Masoala peninsula, north-east Madagascar).

# Leaflet map
m <- leaflet() %>% addTiles() %>%
  addRasterImage(r, colors=pal, opacity=0.8, project=FALSE) %>%
  fitBounds(ext2@xmin,ext2@ymin,ext2@xmax,ext2@ymax)
m

References

Vieilledent, Ghislain, Clovis Grinand, Fety A. Rakotomalala, Rija Ranaivosoa, Jean-Roger Rakotoarijaona, Thomas F. Allnutt, and Frédéric Achard. 2018. “Combining Global Tree Cover Loss Data with Historical National Forest Cover Maps to Look at Six Decades of Deforestation and Forest Fragmentation in Madagascar.” Biological Conservation 222:189–97. https://doi.org/10.1016/j.biocon.2018.04.008.

Vieilledent, Ghislain, Cory Merow, Jérôme Guélat, Andrew M. Latimer, Marc Kéry, Alan E. Gelfand, Adam M. Wilson, Frédéric Mortier, and John A. Silander Jr. 2014. hSDM: hierarchical Bayesian species distribution models. https://doi.org/10.5281/zenodo.594920.

LS0tCnRpdGxlOiAiTW9kZWxsaW5nIGFuZCBmb3JlY2FzdGluZyBkZWZvcmVzdGF0aW9uIgphdXRob3I6ICJHaGlzbGFpbiBWaWVpbGxlZGVudCIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIG51bWJlcl9zZWN0aW9uczogbm8KICAgIHRoZW1lOiBzcGFjZWxhYgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCmJpYmxpb2dyYXBoeTogYmliL2JpYmxpby5iaWIKYmlibGlvLXN0eWxlOiBiaWIvam91cm5hbC1vZi1hcHBsaWVkLWVjb2xvZ3kuY3NsCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoa25pdHIpCmtuaXRyOjpvcHRzX2NodW5rJHNldCgKCWVjaG89VFJVRSwKCW1lc3NhZ2U9RkFMU0UsCgl3YXJuaW5nPUZBTFNFLAoJY2FjaGU9RkFMU0UKKQprbml0cjo6b3B0c19rbml0JHNldCgKICByb290LmRpcj1ycHJvanJvb3Q6OmZpbmRfcnN0dWRpb19yb290X2ZpbGUoKQopCmBgYAoKIyMgSW50cm9kdWN0aW9uCgpgYGB7ciBjYXVzZXMsIGVjaG89RkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJ0cmFpbmluZy9maWd1cmVzL2NhdXNlcy5qcGciKQpgYGAKCkZpZ3VyZSBjYXB0aW9uOiAqKk1haW4gY2F1c2VzIG9mIGRlZm9yZXN0YXRpb24gaW4gY2VudHJhbCBNZW5hYmUuKiogKiphLWEnKio6IF9TbGFzaC1hbmQtYnVybiBhZ3JpY3VsdHVyZSAoImhhdHNha2UiKSBmb3IgcGVhbnV0IGNyb3AuXyBQZWFudXQgKGEnKSBpcyBjdWx0aXZhdGVkIGFzIGEgY2FzaCBjcm9wLiBQYXJ0IG9mIHRoZSBwcm9kdWN0aW9uIGlzIGF0IHRoZSBkZXN0aW5hdGlvbiBvZiB0aGUgbmF0aW9uYWwgbWFya2V0IGJ1dCBtb3N0IG9mIGl0IGlzIGV4cG9ydGVkIG91dHNpZGUgTWFkYWdhc2NhciwgbWFpbmx5IGZvciB0aGUgQ2hpbmVzZSBtYXJrZXQuICoqYi1iJyoqOiBfU2xhc2gtYW5kLWJ1cm4gYWdyaWN1bHR1cmUgZm9yIG1haXplIGNyb3AuXyBtYWl6ZSAoYicpIGlzIGN1bHRpdmF0ZWQgZm9yIGF1dG8tY29uc3VtcHRpb24gYW5kIGFzIGEgY2FzaCBjcm9wLiBUaGUgcHJvZHVjdGlvbiBvZiBtYWl6ZSBpcyBhdCB0aGUgZGVzdGluYXRpb24gb2YgdGhlIG5hdGlvbmFsIG1hcmtldCBhbmQgaXMgdXNlZCBpbiBwYXJ0aWN1bGFyIHRvIGJyZXcgdGhlIG5hdGlvbmFsIGJlZXJzLiAqKmMtYycqKjogX0N5Y2xvbmUgZm9sbG93ZWQgYnkgdW5jb250cm9sbGVkIGZpcmVzLl8gQ3ljbG9uZSBfIkZhbmVsZSJfICgyMDA5KSBjYXVzZWQgdHJlZSBtb3J0YWxpdHkgYW5kIGFjY3VtdWxhdGlvbiBvZiB3b29kIGZ1ZWwgb24gdGhlIGdyb3VuZC4gQXMgYSBjb25zZXF1ZW5jZSwgdW5jb250cm9sbGVkIGZpcmVzIHNldCBvbiBuZWFyYnkgcGFzdHVyZXMgKGMnKSBzcHJlYWQgb3ZlciBsYXJnZSBhcmVhcyBvZiBmb3Jlc3QgYWZ0ZXIgMjAwOS4gKipkLWQnKio6IF9JbGxlZ2FsIGxvZ2dpbmcuXyBUaW1iZXJzIGFyZSB1c2VkIGZvciBob3VzZSBhbmQgYm9hdCBjb25zdHJ1Y3Rpb24uCgpgYGB7ciBjYy1saWNlbnNlLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygidHJhaW5pbmcvZmlndXJlcy9ieS1zYS5wbmciKQpgYGAKClRoaXMgdHV0b3JpYWwgaXMgbGljZW5zZWQgdW5kZXIgdGhlIFtDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uLVNoYXJlQWxpa2UgNC4wIEludGVybmF0aW9uYWwgTGljZW5zZV0oaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktc2EvNC4wLykuCgojIyBDb21wdXRpbmcgZW52aXJvbm1lbnQKCiMjIyBJbnN0YWxsaW5nIERvY2tlcgoKRG9ja2VyIGlzIHVzZWQgdG8gcnVuIHNvZnR3YXJlIHBhY2thZ2VzIGNhbGxlZCAiY29udGFpbmVycyIuIENvbnRhaW5lcnMgYXJlIGlzb2xhdGVkIGZyb20gZWFjaCBvdGhlciBhbmQgYnVuZGxlIHRoZWlyIG93biB0b29scywgbGlicmFyaWVzIGFuZCBjb25maWd1cmF0aW9uIGZpbGVzLiBDb250YWluZXJzIGFyZSBjcmVhdGVkIGZyb20gImltYWdlcyIgdGhhdCBzcGVjaWZ5IHRoZWlyIHByZWNpc2UgY29udGVudHMuCgpUbyBpbnN0YWxsIERvY2tlciBmb3IgeW91ciBvcGVyYXRpbmcgc3lzdGVtLCBnbyB0byA8aHR0cHM6Ly9kb2NzLmRvY2tlci5jb20vaW5zdGFsbC8+LiBGb3IgV2luZG93cyB1c2VyLCBnbyBkaXJlY3RseSB0byA8aHR0cHM6Ly9kb2NzLmRvY2tlci5jb20vZG9ja2VyLWZvci13aW5kb3dzL2luc3RhbGwvPi4KCiMjIyBEb2NrZXIgaW1hZ2UKCkZvciBvdXIgdHV0b3JpYWwsIHdlIGhhdmUgYnVpbHQgYSBzcGVjaWZpYyBEb2NrZXIgaW1hZ2UgaW5jbHVkaW5nIFIgYW5kIFB5dGhvbjoKCi0gUiBjb21wdXRpbmcgZW52aXJvbm1lbnQ6IFIgMy41LjEsIFJTdHVkaW8sIGFuZCB2YXJpb3VzIFIgcGFja2FnZXMgKGV4LiBrbml0ciwgZ2dwbG90MiwgcmV0aWN1bGF0ZSkuCi0gUHl0aG9uIGNvbXB1dGluZyBlbnZpcm9ubWVudDogUHl0aG9uIDMsIGFuZCB2YXJpb3VzIFB5dGhvbiBwYWNrYWdlcyAoZXguIG51bXB5LCBwYW5kYXMsIG1hdHBsb3RsaWIsIGFuZCBmb3Jlc3RhdHJpc2spLgoKU29tZSBjb21wdXRhdGlvbnMsIHN1Y2ggYXMgY29tcHV0YXRpb24gYnkgYmxvY2tzIG9uIGxhcmdlIHJhc3RlciBmaWxlcywgd2VyZSBtb3JlIGVhc2lseSBjb2RlZCBhbmQgZmFzdGVyIHdpdGggUHl0aG9uLiBUaGF0J3Mgd2h5IHdlIGRlY2lkZWQgdG8gdXNlIFB5dGhvbiBhcyBhIGNvbXBsZW1lbnRhcnkgbGFuZ3VhZ2UgdG8gUi4gVGhlIGByZXRpY3VsYXRlYCBSIHBhY2thZ2UgYWxsb3cgdXMgdG8gdXNlIFB5dGhvbiBmdW5jdGlvbnMgZnJvbSBSIGFuZCB0aHVzIHRvIHdvcmsgaW4gYSBzaW5nbGUgY29tcHV0aW5nIGVudmlyb25tZW50IHdpdGhpbiBSU3R1ZGlvLgoKIyMjIExvYWQgdGhlIGBmb3Jlc3RhdHJpc2tgIERvY2tlciBpbWFnZQoKRG93bmxvYWQgdGhlIGltYWdlIGZyb20gaW50ZXJuZXQgd2l0aCB0aGUgZm9sbG93aW5nIGNvbW1hbmQgaW4gYSB0ZXJtaW5hbDogYGRvY2tlciBwdWxsIGdoaXNsYWludi9kb2NrZXItZm9yZXN0YXRyaXNrIC5gIChkbyBub3QgZm9yZ2V0IHRoZSBmaW5hbCAiLiIpCgpOb3RlOiBJZiB0aGUgaW50ZXJuZXQgY29ubmVjdGlvbiBpcyBzbG93LCB1c2UgYSBVU0Iga2V5IGFuZCBjb3B5IHRoZSBpbWFnZSBzb21ld2hlcmUgb24geW91ciBjb21wdXRlciBhbmQgbG9hZCBpdCB3aXRoIHRoZSBmb2xsb3dpbmcgY29tbWFuZDogYGRvY2tlciBsb2FkIC1pIDxwYXRoIHRvIGltYWdlIHRhciBmaWxlPmAKClJ1biB0aGUgY29udGFpbmVyIHdpdGggYGRvY2tlciBydW4gLWQgLWUgUEFTU1dPUkQ9UFdEIC1lIFJPT1Q9WUVTIC1wIDg3ODc6ODc4NyAtdiBPUklHSU5fRk9MREVSOi9ob21lL3JzdHVkaW8gZ2hpc2xhaW52L2RvY2tlci1mb3Jlc3RhdHJpc2tgLiAgCgpOb3RlOgoKLSBTZXQgeW91ciBvd24gcGFzc3dvcmQgYFBXRGAsIGl0IGNhbm5vdCBiZSBgcnN0dWRpb2AuCi0gUmVwbGFjZSBgT1JJR0lOX0ZPTERFUmAgd2l0aCBhIGZvbGRlciBvbiB5b3VyIGxvY2FsIG1hY2hpbmUgd2hlcmUgeW91IHdhbnQgdG8gc2F2ZSB0aGUgUiBzY3JpcHRzLiBGb3IgZXhhbXBsZSBgL2MvVXNlcnMvWU9VUl9OQU1FYCBvciBgL2hvbWUvWU9VUl9OQU1FYC4KClBvaW50IHlvdXIgYnJvd3NlciB0byBgbG9jYWxob3N0Ojg3ODdgIHRvIGFjY2VzcyBSU3R1ZGlvIGludGVyZmFjZS4gTG9nIGluIHdpdGggYHJzdHVkaW8vUFdEYC4KCiMjIyBHZXQgdGhlIHR1dG9yaWFsCgpJbiBSU3R1ZGlvLCBvcGVuIGEgdGVybWluYWwgYW5kIGNsb25lIHRoZSBHaXRIdWIgcmVwb3NpdG9yeSBsb2NhbGx5OiAgCmBnaXQgY2xvbmUgaHR0cHM6Ly9naXRodWIuY29tL2doaXNsYWludi9mb3JlY2FzdGluZy1kZWZvcmVzdGF0aW9uLU1hZGFgCgpOYXZpZ2F0ZSB3aXRoIFJTdHVkaW8gaW4gdGhlIGZvbGRlciBuYW1lZCBgZm9yZWNhc3RpbmctZGVmb3Jlc3RhdGlvbi1NYWRhYCBhbmQgb3BlbiB0aGUgUiBOb3RlYm9vayBmaWxlIG5hbWVkIGB0cmFpbmluZy5SbWRgLiBSIE1hcmtkb3duIGZpbGVzIChgKi5SbWRgKSBjYW4gYmUgdXNlZCBpbiBSIHRvIGVtYmJlZCB0ZXh0LCBjb2RlLCB0YWJsZSBhbmQgZmlndWVzIGluc2lkZSBhIHVuaXF1ZSBkb2N1bWVudC4gQ29kZSBhcmUgb3JnYW5pemVkIGludG8gJ2NodW5rcycgdGhhdCBjYW4gYmUgZXhlY3V0ZWQgaW5kZXBlbmRlbnRseSBhbmQgaW50ZXJhY3RpdmVseS4gQW4gUiBOb3RlYm9vayBpcyBhbiBSIE1hcmtkb3duIGRvY3VtZW50IHdpdGggb3V0cHV0IHZpc2libGUgaW1tZWRpYXRlbHkgYmVuZWF0aCB0aGUgaW5wdXQuIAoKSSB1c2VkIHRoaXMgbm90ZWJvb2sgdG8gd3JpdGUgdGhlIHR1dG9yaWFsIGF2YWlsYWJsZSBhdCA8aHR0cHM6Ly9lY29sb2d5LmdoaXNsYWludi5mci9mb3JlY2FzdGluZy1kZWZvcmVzdGF0aW9uLU1hZGE+LgoKSW4gdGhlIHByZWFtYnVsZSwgY2hhbmdlIHRoZSBhdXRob3IgYW5kIGRhdGUgd2l0aCB5b3VyIG5hbWUgYW5kIHRvZGF5J3MgZGF0ZS4KCiMjIyBMb2FkaW5nIGxpYnJhcmllcyBpbiBSCgpgYGB7ciBSX2xpYnJhcmllc30KIyBFbnZpcm9ubWVudGFsIHZhcmlhYmxlcwpTeXMudW5zZXRlbnYoIkRJU1BMQVkiKSAjIFJlbW92ZSBESVNQTEFZIGZvciBQeXRob24gcGxvdAoKIyBMaWJyYXJpZXMKcmVxdWlyZShyZXRpY3VsYXRlKQpyZXF1aXJlKGdsdWUpCnJlcXVpcmUoY3VybCkKcmVxdWlyZShkcGx5cikKcmVxdWlyZShicm9vbSkKcmVxdWlyZShnZ3Bsb3QyKQpyZXF1aXJlKHJhc3RlcikKcmVxdWlyZShyYXN0ZXJWaXMpCnJlcXVpcmUocmdkYWwpCnJlcXVpcmUobGVhZmxldCkKYGBgCgpgYGB7ciBzb3VyY2VfUn0KIyBTb3VyY2UgUiBwbG90dGluZyBmdW5jdGlvbnMKc291cmNlKCJSL2RmcF9wbG90LlIiKQpgYGAKCiMjIyBJbXBvcnRpbmcgUHl0aG9uIG1vZHVsZXMgaW4gUgoKU3BlY2lmeSB0aGUgUHl0aG9uIHZlcnNpb24gdG8gdXNlIGFuZCBjaGVjayB0aGF0IGl0IGlzIHRoZSByaWdodCBvbmUuCgpgYGB7cn0KdXNlX3B5dGhvbigiL3Vzci9iaW4vcHl0aG9uMy41IikKcHlfY29uZmlnKCkKYGBgCgpJbXBvcnQgdGhlIFB5dGhvbiBtb2R1bGVzIGludG8gUi4KCmBgYHtyIGltcG9ydHB5fQpmYXIgPC0gaW1wb3J0KCJmb3Jlc3RhdHJpc2siKQpwYXRzeSA8LSBpbXBvcnQoInBhdHN5IikKc20gPC0gaW1wb3J0KCJzdGF0c21vZGVscy5hcGkiKQpzbWYgPC0gaW1wb3J0KCJzdGF0c21vZGVscy5mb3JtdWxhLmFwaSIpCgpgYGAKCiMjIyBSL1B5dGhvbiBpbnRybwoKIyMjIyBSCgojIyMjIyBTb21lIGxpbmtzCgotIDxodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9vbmxpbmUtbGVhcm5pbmcvI3ItcHJvZ3JhbW1pbmc+Ci0gPGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL2RvYy9jb250cmliL1BhcmFkaXMtcmRlYnV0c19lbi5wZGY+IChhbHNvIGluIEZyZW5jaDogX2ZyLnBkZikKLSBDaGVhdCBzaGVldHM6IDxodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHM+CgojIyMjIyBTb21lIGV4ZXJjaXNlcwoKYGBge3IgUl9leGVyY2lzZXN9CmEgPSBjKDIwLDUsNiwyLDksMTIpCmIgPSAxCmMgPSBhICsgYgpwcmludChjKQpgYGAKCgojIyMjIyBTaW1wbGUgcmVncmVzc2lvbgoKYGBge3IgUl9yZWdyZXNzaW9ufQojIEdlbmVyYXRpbmcgZGF0YQpub2JzIDwtIDEwMAp4LnNlcSA8LSBydW5pZihuPW5vYnMsbWluPTAsbWF4PTEwMCkKYS50YXJnZXQgPC0gMgpiLnRhcmdldCA8LSAyCnNpZ21hMi50YXJnZXQgPC0gMzAwCnkuc2VxIDwtIGEudGFyZ2V0ICsgYi50YXJnZXQqeC5zZXEgKwogICAgICAgICBybm9ybShuPW5vYnMsbWVhbj0wLHNkPXNxcnQoc2lnbWEyLnRhcmdldCkpCgojIERhdGEtc2V0CkRhdGEgPC0gYXMuZGF0YS5mcmFtZShjYmluZCh5LnNlcSx4LnNlcSkpCgojIFBsb3QKcGxvdCh4LnNlcSx5LnNlcSkKCiMgRXN0aW1hdGlvbgpNIDwtIGxtKHkuc2Vxfnguc2VxLGRhdGE9RGF0YSkKc3VtbWFyeShNKQpNJGNvZWZmaWNpZW50cwp2YXIoTSRyZXNpZHVhbHMpCk0kZGYKCiMgR3JhcGhpY3MgCngucHJlZCA8LSBzZXEoZnJvbT0wLHRvPTEwMCxsZW5ndGgub3V0PTEwMCkgIyBXZSB3aWxsIHByZWRpY3QgeSBmb3IgMTAwIHZhbHVlcyBvZiB4ClgucHJlZCA8LSBhcy5kYXRhLmZyYW1lKHgucHJlZCkKbmFtZXMoWC5wcmVkKSA8LSAieC5zZXEiClkucHJlZCA8LSBwcmVkaWN0LmxtKE0sWC5wcmVkLGludGVydmFsPSJjb25maWRlbmNlIiwKICAgICAgICAgICAgICAgICAgICAgdHlwZT0icmVzcG9uc2UiKQoKcGxvdCh4LnNlcSx5LnNlcSxjb2w9Z3JleSgwLjcpLAogICAgIHhsYWI9IlgiLAogICAgIHlsYWI9IlkiLAogICAgIG1haW49IlNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiIpICMgUm93IGRhdGEKCiMgQ29uZmlkZW5jZSBlbnZlbG9wcyBmb3IgcHJlZGljdGl2ZSBwb3N0ZXJpb3IKbGluZXMoeC5wcmVkLFkucHJlZFssMV0sY29sPSJyZWQiLGx3ZD0zKQpsaW5lcyh4LnByZWQsWS5wcmVkWywyXSxjb2w9ImJsdWUiLGx3ZD0yLGx0eT0yKQpsaW5lcyh4LnByZWQsWS5wcmVkWywzXSxjb2w9ImJsdWUiLGx3ZD0yLGx0eT0yKQoKYGBgCgojIyMjIFB5dGhvbgoKIyMjIyMgU29tZSBsaW5rcwoKLSA8aHR0cHM6Ly93d3cucHl0aG9uLm9yZy9hYm91dC9nZXR0aW5nc3RhcnRlZD4KLSA8aHR0cHM6Ly93d3cucHl0aG9uc2hlZXRzLmNvbT4KCiMjIyMjIFNvbWUgZXhlcmNpc2VzCgpZb3UgY2FuIGV4ZWN1dGUgUHl0aG9uIGNvZGUgd2l0aGluIGEgUiBub3RlYm9va3MuCgpgYGB7cHl0aG9ufQppbXBvcnQgbnVtcHkgYXMgbnAKYSA9IG5wLmFycmF5KFsyMCw1LDYsMiw5LDEyXSkKYiA9IDEKYyA9IGEgKyBiCnByaW50KHR5cGUoYykpCnByaW50KGMpCmBgYAoKIyMgUHJlcGFyaW5nIHRoZSBkYXRhLXNldAoKIyMjIERvd25sb2FkaW5nIHRoZSBnZW9zcGF0aWFsIGRhdGEKClRvIG1vZGVsIHRoZSBzcGF0aWFsIHByb2JhYmlsaXR5IG9mIGRlZm9yZXN0YXRpb24sIHdlIG5lZWQgYSBtYXAgb2YgdGhlIHBhc3QgZGVmb3Jlc3RhdGlvbiBhbmQgbWFwcyBvZiBwb3RlbnRpYWwgZXhwbGljYXRpdmUgZW52aXJvbm1lbnRhbCB2YXJpYWJsZXMuIEVudmlyb25tZW50YWwgdmFyaWFibGVzIGNhbiBiZSBkZXJpdmVkIGZyb20gdG9wb2dyYXBoeSAoYWx0aXR1ZGUgYW5kIHNsb3BlKSwgYWNjZXNzaWJpbGl0eSAoZGlzdGFuY2UgdG8gcm9hZHMsIHRvd25zLCBhbmQgZm9yZXN0IGVkZ2UpLCBkZWZvcmVzdGF0aW9uIGhpc3RvcnkgKGRpc3RhbmNlIHRvIHByZXZpb3VzIGRlZm9yZXN0YXRpb24pIG9yIGxhbmRzY2FwZSBwb2xpY3kgKHByb3RlY3RlZCBhcmVhIG5ldHdvcmspIGZvciBleGFtcGxlLiBJbiBvdXIgZXhhbXBsZSwgd2UgdXNlIHRoZSBmb2xsb3dpbmcgdmFyaWFibGVzIDoKCmBgYHtyIHZhcmlhYmxlc30KbGlicmFyeShrYWJsZUV4dHJhKQp0YWIxIDwtIHJlYWQudGFibGUoInRyYWluaW5nL3RhYmxlcy92YXJpYWJsZXMudHh0IixzZXA9IiwiLGhlYWRlcj1UUlVFKQpuYW1lcyh0YWIxKSA8LSBjKCJQcm9kdWN0IiwiU291cmNlIiwiVmFyaWFibGUgZGVyaXZlZCIsIlVuaXQiLCJSZXNvbHV0aW9uIChtKSIpCiMgS2FibGUKa25pdHI6OmthYmxlKHRhYjEsIGxpbmVzZXA9IiIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIikpCmBgYAoKMS4gPGh0dHA6Ly9iaW9zY2VuZW1hZGEuY2lyYWQuZnI+LCBkZWZvcmVzdGF0aW9uIG1hcHMgZnJvbSBAVmllaWxsZWRlbnQyMDE4CjIuIDxodHRwOi8vc3J0bS5jc2kuY2dpYXIub3JnPiwgU1JUTSA5MG0gRGlnaXRhbCBFbGV2YXRpb24gRGF0YWJhc2UgdjQuMQozLiA8aHR0cDovL3d3dy5nZW9mYWJyaWsuZGU+LCBkYXRhIGV4dHJhY3RzIGZyb20gdGhlIE9wZW5TdHJlZXRNYXAgcHJvamVjdCBmb3IgTWFkYWdhc2NhciwKNC4gPGh0dHA6Ly9yZWJpb21hLm5ldD4sIFNBUE0gKCJTeXN0w6htZSBkZXMgQWlyZXMgUHJvdMOpZ8OpZXMgw6AgTWFkYWdhc2NhciIpLCAyMC8xMi8yMDEwIHZlcnNpb24KCgpgYGB7ciBkb3dubG9hZF9kYXRhfQojIE1ha2UgZGF0YSBkaXJlY3RvcnkKaWYgKCFkaXIuZXhpc3RzKCJkYXRhIikpIHsKICBkaXIuY3JlYXRlKCJkYXRhIikKICBkaXIuY3JlYXRlKCJkYXRhL21vZGVsIikKICBkaXIuY3JlYXRlKCJkYXRhL21hZGEiKQogIGRpci5jcmVhdGUoImRhdGEvdmFsaWRhdGlvbiIpCn0KCiMgRmlsZXMKZmlsZXMgPC0gYygiZm9yZGVmb3IyMDEwLnRpZiIsICJkaXN0X2VkZ2UudGlmIiwgImRpc3RfZGVmb3IudGlmIiwKICAgICAgICAgICAiYWx0aXR1ZGUudGlmIiwgInNsb3BlLnRpZiIsICJhc3BlY3QudGlmIiwKICAgICAgICAgICAiZGlzdF9yb2FkLnRpZiIsICJkaXN0X3Rvd24udGlmIiwgImRpc3Rfcml2ZXIudGlmIiwKICAgICAgICAgICAic2FwbS50aWYiLCAicm9hZHMua21sIiwgInRvd25zLmttbCIsICJyaXZlcnMua21sIiwgInNhcG0ua21sIikKCiMgRG93bmxvYWQgbW9kZWwgZGF0YQpmb3IgKGkgaW4gZmlsZXMpIHsKICBpZiAoIWZpbGUuZXhpc3RzKGdsdWUoImRhdGEvbW9kZWwve2l9IikpKSB7CiAgICBmIDwtIGdsdWUoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmQvMjU5NTgyL2ZpbGVzL3tpfSIpCiAgICBjdXJsX2Rvd25sb2FkKGYsIGdsdWUoImRhdGEvbW9kZWwve2l9IiksIHF1aWV0PUZBTFNFKQogIH0KfQoKIyBEb3dubG9hZCB2YWxpZGF0aW9uIGRhdGEKaWYgKCFmaWxlLmV4aXN0cygiZGF0YS92YWxpZGF0aW9uL2ZvcjIwMTcudGlmIikpIHsKICBmIDwtICJodHRwOi8vYmlvc2NlbmVtYWRhLmNpcmFkLmZyL0ZpbGVUcmFuc2Zlci9mb3IyMDE3LnRpZiIKICBjdXJsX2Rvd25sb2FkKGYsICJkYXRhL3ZhbGlkYXRpb24vZm9yMjAxNy50aWYiLCBxdWlldD1GQUxTRSkKfQoKIyBEb3dubG9hZCBtYWRhIG91dGxpbmUKaWYgKCFmaWxlLmV4aXN0cygiZGF0YS9tYWRhL21hZGEzOHMuc2hwIikpIHsKICBmIDwtICJodHRwOi8vYmlvc2NlbmVtYWRhLmNpcmFkLmZyL0ZpbGVUcmFuc2Zlci9tYWRhMzhzLnppcCIKICBjdXJsX2Rvd25sb2FkKGYsICJkYXRhL21hZGEvbWFkYTM4cy56aXAiLCBxdWlldD1GQUxTRSkKICB1bnppcCgiZGF0YS9tYWRhL21hZGEzOHMuemlwIiwgZXhkaXI9ImRhdGEvbWFkYSIpCn0KCmBgYAoKSW4gb3VyIGV4YW1wbGUsIGBmb3JkZWZvcjIwMTAudGlmYCBpcyBhIGZvcmVzdCByYXN0ZXIgYXQgMzBtIGZvciB0aGUgeWVhciAyMDEwIGNvbnNpZGVyaW5nIHRoZSBkZWZvcmVzdGF0aW9uIG9uIHRoZSBwZXJpb2QgMjAwMC0yMDEwIGluIE1hZGFnYXNjYXIuIFdlIGNhbiBwbG90IHRoaXMgcmFzdGVyIGFuZCB6b29tIG9uIGEgcmVnaW9uIHdpdGggZnVuY3Rpb24gYC5wbG90LmZvcmVzdCgpYCBpbiB0aGUgYGZvcmVzdGF0cmlza2AgcGFja2FnZSB0byBzZWUgdGhlIGRlZm9yZXN0YXRpb24gZGF0YS4gVGhlIHJlbWFpbmluZyBmb3Jlc3QgYXBwZWFycyBpbiBncmVlbiBhbmQgdGhlIGRlZm9yZXN0ZWQgYXJlYXMgYXBwZWFyIGluIHJlZC4KCmBgYHtyIHBsb3RfZmNjfQojIFBsb3QgZm9yZXN0IGNvdmVyIGNoYW5nZSAyMDAwLTIwMTAKZmlnIDwtIGZhciRwbG90JGZjYyhpbnB1dF9mY2NfcmFzdGVyPSJkYXRhL21vZGVsL2ZvcmRlZm9yMjAxMC50aWYiLAogICAgICAgICAgICAgb3V0cHV0X2ZpbGU9Im91dHB1dC9mY2MucG5nIiwKICAgICAgICAgICAgIGNvbD1jKDI1NSwwLDAsMjU1KSwgICMgcmdiYSBjb2xvciBmb3IgZGVmb3Jlc3RhdGlvbgogICAgICAgICAgICAgZmlnc2l6ZT1jKDUsNSksCiAgICAgICAgICAgICBkcGk9MTUwLAogICAgICAgICAgICAgem9vbT1jKDM0MDAwMCw0MTIwMDAsNzQyMDAwMCw3NTAwMDAwKSkKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIm91dHB1dC9mY2MucG5nIikKYGBgCgojIyMgU2FtcGxpbmcgcG9pbnRzCgpXZSB1c2UgdGhlIGZ1bmN0aW9uIGAuc2FtcGxlKClgIGZyb20gdGhlIGBmb3Jlc3RhdHJpc2tgIG1vZHVsZSB0byBzYW1wbGUgMTAsMDAwIHBvaW50cyAocGl4ZWwgY2VudGVycykgaW4gdGhlIGRlZm9yZXN0ZWQgYXJlYXMgYW5kIGluIHRoZSByZW1haW5pbmcgZm9yZXN0ICgyMCwwMDAgcG9pbnRzIGluIHRvdGFsKS4gVGhlIGBpbnB1dF9mb3Jlc3RfcmFzdGVyYCBhcmd1bWVudCBkZWZpbmVzIHRoZSBwYXRoIHRvIHRoZSBmb3Jlc3QgcmFzdGVyIGluY2x1ZGluZyB0aGUgZGVmb3Jlc3RlZCBwaXhlbHMgKHdpdGggdmFsdWU9MCkgYW5kIHRoZSByZW1haW5pbmcgZm9yZXN0IHBpeGVscyAodmFsdWU9MSkgYWZ0ZXIgYSBnaXZlbiBwZXJpb2Qgb2YgdGltZS4gVGhlIHJhbmRvbSBzZWVkIGNhbiBiZSBzZXQgd2l0aCBhcmd1bWVudCBgU2VlZGAgdG8gcmVwcm9kdWNlIHRoZSBkYXRhIG9mIHRoZSByYW5kb20gc2FtcGxpbmcuCgpUaGUgYC5zYW1wbGUoKWAgZnVuY3Rpb24gYWxzbyBleHRyYWN0cyBpbmZvcm1hdGlvbiBmcm9tIGVudmlyb25tZW50YWwgdmFyaWFibGVzIGZvciBlYWNoIHNhbXBsZWQgcG9pbnQuIFNhbXBsaW5nIGlzIGRvbmUgYnkgYmxvY2sgdG8gYWxsb3cgdGhlIGNvbXB1dGF0aW9uIG9uIGxhcmdlIHN0dWR5IGFyZWFzIChlLmcuIGNvdW50cnkgb3IgY29udGluZW50YWwgc2NhbGUpIHdpdGggYSBmaW5lIHNwYXRpYWwgcmVzb2x1dGlvbiAoZS5nLiAzMG0pLiBUaGUgYHZhcl9kaXJgIGFyZ3VtZW50IGluZGljYXRlcyB0aGUgZGlyZWN0b3J5IGluY2x1ZGluZyBhbGwgdGhlIGVudmlyb25tZW50YWwgcmFzdGVyIGZpbGVzICh0aGV5IG11c3QgYmUgR2VvVElGRiBmaWxlcyB3aXRoIGV4dGVuc2lvbiBgLnRpZmApIHRoYXQgd2Ugd2FudCB0byB0ZXN0LgoKVGhlIGAuc2FtcGxlKClgIGZ1bmN0aW9uIGlkZW50aWZpZXMgdGhlIHNwYXRpYWwgY2VsbCBmb3IgZWFjaCBzYW1wbGUgcG9pbnQgKHNhbXBsZSBwb2ludCBhcmUgZ3JvdXBlZCB3aXRoaW4gYSBzcGF0aWFsIGNlbGwpLiBTcGF0aWFsIGNlbGxzIGFuZCBncm91cGVkIG9ic2VydmF0aW9ucyBhcmUgdXNlZCB0byBlc3RpbWF0ZSB0aGUgc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gb2YgdGhlIGRlZm9yZXN0YXRpb24gcHJvY2Vzcy4gVGhlIGBjc2l6ZWAgYXJndW1lbnQgZGVmaW5lIHRoZSB3aWR0aCAoaW4ga20pIG9mIHRoZSBzcXVhcmUgc3BhdGlhbCBjZWxscy4gRWFjaCBzcGF0aWFsIGNlbGwgd2lsbCBiZSBhdHRyaWJ1dGVkIGEgcGFyYW1ldGVyLiBUbyBhdm9pZCBlc3RpbWF0aW5nIHRvbyBtYW55IHBhcmFtZXRlcnMsIHdpZHRoIG9mIHRoZSBzcXVhcmUgc3BhdGlhbCBjZWxscyBtdXN0IGJlIHN1ZmZpY2llbnRseSBsYXJnZS4gQm90aCBwb2ludHMgc2FtcGxpbmcgYW5kIGV4dHJhY3Rpb24gb2YgZW52aXJvbm1lbnRhbCBkYXRhIGFyZSBkb25lIGJ5IGJsb2NrIHRvIGF2b2lkIG1lbW9yeSBwcm9ibGVtcyBmb3IgYmlnIGRhdGFzZXRzLgoKYGBge3Igc2FtcGxpbmd9CiMgTWFrZSBvdXRwdXQgZGlyZWN0b3J5CmlmICghZGlyLmV4aXN0cygib3V0cHV0IikpIHsKICBkaXIuY3JlYXRlKCJvdXRwdXQiKQp9CgojIFRyYWluaW5nIGRhdGEtc2V0CmlmICghZmlsZS5leGlzdHMoIm91dHB1dC9zYW1wbGUudHh0IikpIHsKICBzYW1wIDwtIGZhciRzYW1wbGUobnNhbXA9MTAwMDBMLCBTZWVkPTEyMzRMLCBjc2l6ZT0xMEwsCiAgICAgICAgICAgICAgICAgICAgIHZhcl9kaXI9ImRhdGEvbW9kZWwiLAogICAgICAgICAgICAgICAgICAgICBpbnB1dF9mb3Jlc3RfcmFzdGVyPSJmb3JkZWZvcjIwMTAudGlmIiwKICAgICAgICAgICAgICAgICAgICAgb3V0cHV0X2ZpbGU9Im91dHB1dC9zYW1wbGUudHh0IiwKICAgICAgICAgICAgICAgICAgICAgYmxrX3Jvd3M9MUwpCn0Kc2FtcCA8LSByZWFkLnRhYmxlKCJvdXRwdXQvc2FtcGxlLnR4dCIsIGhlYWRlcj1UUlVFLCBzZXA9IiwiKQpzZXQuc2VlZCgxMjM0KQp0cmFpbiA8LSBzYW1wbGUoMToyMDAwMCwgc2l6ZT0xMDAwMCwgcmVwbGFjZT1GQUxTRSkKZGF0YV90cmFpbiA8LSBzYW1wW3RyYWluLF0gJT4lIGRwbHlyOjpmaWx0ZXIoY29tcGxldGUuY2FzZXMoLikpCmRhdGFfdmFsaWQgPC0gc2FtcFstdHJhaW4sXSAlPiUgZHBseXI6OmZpbHRlcihjb21wbGV0ZS5jYXNlcyguKSkKaGVhZChkYXRhX3RyYWluKQoKYGBgCgpTYW1wbGVkIG9ic2VydmF0aW9ucyBjYW4gYmUgcGxvdHRlZCB1c2luZyBmdW5jdGlvbiBgLnBsb3Qub2JzKClgIGZyb20gdGhlIGRlZm9yZXN0cHJvYiBtb2R1bGUuIERhcmsgcmVkIGRvdHMgaW5kaWNhdGUgZGVmb3Jlc3RhdGlvbiBvYnNlcnZhdGlvbnMgYW5kIGRhcmsgZ3JlZW4gZG90cyBpbmRpY2F0ZSBmb3Jlc3Qgb2JzZXJ2YXRpb25zLgoKYGBge3IgcGxvdF9zYW1wbGV9CiMgUGxvdCBzYW1wbGUgcG9pbnRzCmZpZyA8LSBmYXIkcGxvdCRvYnMoc2FtcGxlPWRhdGFfdHJhaW4sCiAgICAgICAgICAgICBuYW1lX2ZvcmVzdF92YXI9ImZvcmRlZm9yMjAxMCIsCiAgICAgICAgICAgICBpbnB1dF9mY2NfcmFzdGVyPSJkYXRhL21vZGVsL2ZvcmRlZm9yMjAxMC50aWYiLAogICAgICAgICAgICAgb3V0cHV0X2ZpbGU9Im91dHB1dC9vYnMucG5nIiwKICAgICAgICAgICAgIHpvb209YygzNDAwMDAsNDEyMDAwLDc0MjAwMDAsNzUwMDAwMCksCiAgICAgICAgICAgICBmaWdzaXplPWMoNSw1KSwjYygxMS42OSw4LjI3KSwKICAgICAgICAgICAgIHM9NSxkcGk9MzAwKQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygib3V0cHV0L29icy5wbmciKQpgYGAKCgojIyMgRGVzY3JpcHRpdmUgc3RhdGlzdGljcwoKQmVmb3JlIG1vZGVsbGluZyB0aGUgZGVmb3Jlc3RhdGlvbiwgaXQgaXMgaW1wb3J0YW50IHRvIGxvb2sgYXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGVudmlyb25tZW50YWwgdmFyaWFibGVzIGFuZCBkZWZvcmVzdGF0aW9uLiBVc2luZyBmb3JtdWxhcyBmcm9tIHRoZSBbYHBhdHN5YF0oaHR0cHM6Ly9naXRodWIuY29tL3B5ZGF0YS9wYXRzeSkgUHl0aG9uIG1vZHVsZSwgd2UgY2FuIHNwZWNpZnkgdGhlIHJlbGF0aW9uc2hpcHMgdGhhdCB3ZSB3YW50IHRvIGxvb2sgYXQuIEluIHRoZSBleGFtcGxlIGJlbG93LCB3ZSBwbG90IHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gc29tZSBjb250aW51b3VzIGVudmlyb25tZW50YWwgdmFyaWFibGVzIGFuZCB0aGUgcHJvYmFiaWxpdHkgb2YgZGVmb3Jlc3RhdGlvbiB1c2luZyBmdW5jdGlvbiBgLnBsb3QuY29ycmVsYXRpb24oKWAgZnJvbSB0aGUgYGZvcmVzdGF0cmlza2AgcGFja2FnZS4gTm90ZSB0aGF0IC0xIG11c3QgYmUgc2V0IGF0IHRoZSBlbmQgb2YgdGhlIGZvcm11bGEuIFRoZSBmdW5jdGlvbiBgLmNvcnJlbGF0aW9uKClgIHJldHVybnMgYSBzZXJpZSBvZiBncmFwaGljcyB0aGF0IGNhbiBiZSBhbmFseXplZCB0byBjaG9vc2UgdGhlIHJpZ2h0IHJlbGF0aW9uc2hpcCBmb3IgZWFjaCBjb250aW51b3VzIHZhcmlhYmxlIChsaW5lYXIgb3IgcG9seW5vbWlhbCBmb3IgZXhhbXBsZSkuCgpgYGB7ciBjb3JyZWxhdGlvbnN9CiMgRGVzY3JpcHRpdmUgc3RhdGlzdGljcwoKIyBNb2RlbCBmb3JtdWxhcwpmb3JtdWxhXzEgPC0gcGFzdGUwKCJmb3JkZWZvcjIwMTAgfiBkaXN0X3JvYWQgKyBkaXN0X3Rvd24gKyBkaXN0X2RlZm9yICsiLAogICAgICAgICAgICAgICAgICAgICJkaXN0X3JpdmVyICsgZGlzdF9lZGdlICsgYWx0aXR1ZGUgKyBzbG9wZSArIGFzcGVjdCAtIDEiKQojIFN0YW5kYXJkaXplZCB2YXJpYWJsZXMgKG1lYW49MCwgc3RkPTEpCmZvcm11bGFfMiA8LSBwYXN0ZTAoImZvcmRlZm9yMjAxMCB+IHNjYWxlKGRpc3Rfcm9hZCkgKyBzY2FsZShkaXN0X3Rvd24pICsiLAogICAgICAgICAgICAgICAgICAgICJzY2FsZShkaXN0X2RlZm9yKSArIHNjYWxlKGRpc3Rfcml2ZXIpICsgc2NhbGUoZGlzdF9lZGdlKSArIiwKICAgICAgICAgICAgICAgICAgICAic2NhbGUoYWx0aXR1ZGUpICsgc2NhbGUoc2xvcGUpICsgc2NhbGUoYXNwZWN0KSAtIDEiKQpmb3JtdWxhcyA8LSBjKGZvcm11bGFfMSwgZm9ybXVsYV8yKQoKIyBMaXN0IHRvIHN0b3JlIGZpZ3VyZXMKY29ycl9maWcgPC0gbGlzdCgpCgojIExvb3Agb24gZm9ybXVsYXMKZm9yIChmIGluIDE6bGVuZ3RoKGZvcm11bGFzKSkgewogICAgIyBPdXRwdXQgZmlsZQogICAgb2YgPC0gZ2x1ZSgib3V0cHV0L2NvcnJlbGF0aW9uX3tmfS5wZGYiKQogICAgIyBEYXRhCiAgICBkbWF0IDwtIHBhdHN5JGRtYXRyaWNlcyhmb3JtdWxhc1tmXSwgZGF0YT1kYXRhX3RyYWluLCBldmFsX2Vudj0tMUwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm5fdHlwZT0iZGF0YWZyYW1lIikKICAgICMgUGxvdHMKICAgIGZpZyA8LSBmYXIkcGxvdCRjb3JyZWxhdGlvbih5PWRtYXRbWzFdXSxkYXRhPWRtYXRbWzJdXSwKICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RzX3Blcl9wYWdlPTNMLGZpZ3NpemU9Yyg3LDgpLAogICAgICAgICAgICAgICAgICAgICAgICAgZHBpPTEwMEwsb3V0cHV0X2ZpbGU9b2YpCn0KYGBgCgpJbiB0aGlzIGV4YW1wbGUgKHNlZSBwZGYgZmlsZXMgcHJvZHVjZWQpLCB3ZSBjYW4gc2VlIHRoYXQgYSBsaW5lYXIgbW9kZWwgc2hvdWxkIGJlIHN1ZmZpY2llbnQgdG8gcmVwcmVzZW50IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgcHJvYmFiaWxpdHkgb2YgZGVmb3Jlc3RhdGlvbiBhbmQgdGhlIHN0YW5kYXJkaXplZCBkaXN0YW5jZSB0byB0aGUgbmVhcmVzdCByb2FkIG9yIHRvd24uIE9uIHRoZSBjb250cmFyeSwgaXQgY291bGQgYmUgaW50ZXJlc3RpbmcgdG8gZml0IGEgcG9seW5vbWlhbCBtb2RlbCBmb3IgdGhlIHRoZSBzdGFuZGFyZGl6ZWQgZGlzdGFuY2UgdG8gcHJldmlvdXMgZGVmb3Jlc3RhdGlvbiAoYGRpc3RfZGVmb3JgIHZhcmlhYmxlKSBmb3Igd2hpY2ggdGhlIHJlbGF0aW9uc2hpcCBzZWVtcyBub24tbGluZWFyLiBTZXZlcmFsIG1vZGVscyBjYW4gYmUgZml0dGVkIGFuZCBjb21wYXJlZCB0byBzZWUgaWYgYSBzZWNvbmQtb3JkZXIgb3IgdGhpcmQtb3JkZXIgcG9seW5vbWlhbCByZWxhdGlvbnNoaXAgaXMganVzdGlmaWVkLgoKIyMgRGVmb3Jlc3RhdGlvbiBtb2RlbAoKV2UgcHJvcG9zZSB0byB1c2UgdGhlICoqQmlub21pYWwgaUNBUioqIG1vZGVsIFtAVmllaWxsZWRlbnQyMDE0XSB0byBlc3RpbWF0ZSB0aGUgZGVmb3Jlc3RhdGlvbiBwcm9iYWJpbGl0eSBvZiBhIHBpeGVsIGdpdmVuIGEgc2V0IG9mIGVudmlyb25tZW50YWwgdmFyaWFibGVzLiBUaGUgQmlub21pYWwgaUNBUiBtb2RlbCBpcyBhIGxpbmVhciBCaW5vbWlhbCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIGluY2x1ZGluZyBhbiBpbnRyaW5zaWMgQ29uZGl0aW9uYWwgQXV0b3JlZ3Jlc3NpdmUgKGlDQVIpIHByb2Nlc3MgdG8gYWNjb3VudCBmb3IgdGhlIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIG9mIHRoZSBvYnNlcnZhdGlvbnMuIFBhcmFtZXRlciBpbmZlcmVuY2UgaXMgZG9uZSBpbiBhIGhpZXJhcmNoaWNhbCBCYXllc2lhbiBmcmFtZXdvcmsuIFRoZSBgLm1vZGVsX2Jpbm9taWFsX2lDQVIoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYGZvcmVzdGF0cmlza2AgbW9kdWxlIGluY2x1ZGVzIGEgTWV0cm9wb2xpcy13aXRoaW4tR2liYnMgYWxnb3JpdGhtIHdyaXR0ZW4gaW4gcHVyZSBDIGNvZGUgdG8gcmVkdWNlIGNvbXB1dGF0aW9uIHRpbWUuCgpgYGB7ciBCYXllcywgZWNobz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoInRyYWluaW5nL2ZpZ3VyZXMvQmF5ZXMuanBnIikKYGBgCgpGaWd1cmUgY2FwdGlvbjogKipQYXJhbWV0ZXIgaW5mZXJlbmNlIGlzIGRvbmUgaW4gYSBoaWVyYXJjaGljYWwgQmF5ZXNpYW4gZnJhbWV3b3JrLioqIEJheWVzaWFuIHN0YXRpc3RpY3MgcmVseSBvbiB0aGUgW0JheWVzJyB0aGVvcmVtXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9CYXllcydfdGhlb3JlbSkgbmFtZWQgYWZ0ZXIgW1JldmVyZW5kIFRob21hcyBCYXllc10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVGhvbWFzX0JheWVzKS4gRWFjaCBwYXJhbWV0ZXIgaGFzIGEgcHJpb3IgYW5kIGFuIGFwcHJveGltYXRlZCBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIGZyb20gd2hpY2ggd2UgY2FuIGNvbXB1dGUgdGhlIG1lYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgY3JlZGlibGUgaW50ZXJ2YWxzIGF0IDk1JSwgZXRjLgoKRm9yIHRoZSBkZWZvcmVzdGF0aW9uIHByb2Nlc3MgaXQgaXMgXyoqdmVyeSBpbXBvcnRhbnQgdG8gdGFrZSBpbnRvIGFjY291bnQgdGhlIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIG9mIHRoZSBwcm9jZXNzKipfIHdpdGggc3BhdGlhbCByYW5kb20gZWZmZWN0cy4gSW5kZWVkLCB0aGUgc2VsZWN0ZWQgZml4ZWQgZW52aXJvbm1lbnRhbCB2YXJpYWJsZXMgYXJlIG5vdCBhYmxlIHRvIGZ1bGx5IGV4cGxhaW4gdGhlIHNwYXRpYWwgdmFyaWFiaWxpdHkgb2YgdGhlIGRlZm9yZXN0YXRpb24gcHJvY2VzcywgZXNwZWNpYWxseSB3aGVuIHdvcmtpbmcgYXQgbGFyZ2UgZ2VvZ3JhcGhpY2FsIHNjYWxlcywgc3VjaCBhcyB0aGUgbmF0aW9uYWwgb3IgY29udGluZW50YWwgc2NhbGUuIFNwYXRpYWwgcmFuZG9tIGVmZmVjdHMgYWxsb3cgZXN0aW1hdGluZyBhIGhpZ2hlci9sb3dlciBwcm9iYWJpbGl0eSBvZiBkZWZvcmVzdGF0aW9uIGluIGEgcGFydGljdWxhciByZWdpb24gKGFzc29jaWF0ZWQgdG8gKip1bm1lYXN1cmFibGUqKiBvciAqKnVua25vdyoqIGZhY3RvcnMpIHRoYXQgaXMgZGlmZmVyZW50IGZyb20gdGhlIG1lYW4gcHJvYmFiaWxpdHkgb2YgZGVmb3Jlc3RhdGlvbiBkZXJpdmVkIGZyb20gdGhlIGVudmlyb25tZW50YWwgZmFjdG9ycyBpbmNsdWRlZCBpbiB0aGUgbW9kZWwuIFRoZSBCaW5vbWlhbCBpQ0FSIG1vZGVsIGNhbiBiZSBkZXNjcmliZWQgYXMgZm9sbG93OgoKKipFY29sb2dpY2FsIHByb2Nlc3MqKgoKXGJlZ2lue2VxdWF0aW9ufQp5X2kgXHNpbSBcbWF0aGNhbHtCfWlub21pYWwoXHRoZXRhX2ksdF9pKSBcXApcdGV4dHtsb2dpdH0oXHRoZXRhX2kpID0gWF9pIFxiZXRhICsgXHJob197aihpKX0KXGVuZHtlcXVhdGlvbn0KCiR5X2kkOiByYW5kb20gdmFyaWFibGUgZm9yIHRoZSBkZWZvcmVzdGF0aW9uIHByb2Nlc3MgKDAgaWYgbm8gZGVmb3Jlc3RhdGlvbiwgMSBpZiBkZWZvcmVzdGF0aW9uKQoKJFx0aGV0YV9pJDogcHJvYmFiaWxpdHkgb2YgZGVmb3Jlc3RhdGlvbgoKJHRfaSQ6IG51bWJlciBvZiB0cmlhbHMgKGFsd2F5cyAxIGluIG91ciBleGFtcGxlKQoKJFhfaSQ6IHZlY3RvciBvZiB2YWx1ZXMgZm9yIGVudmlyb25tZW50YWwgZXhwbGljYXRpdmUgdmFyaWFibGVzCgokXGJldGEkOiB2ZWN0b3Igb2YgZml4ZWQgZWZmZWN0IHBhcmFtZXRlcnMKCiRccmhvX2okOiBzcGF0aWFsIHJhbmRvbSBlZmZlY3QKICAKJGooaSkkOiBpbmRleCBvZiB0aGUgc3BhdGlhbCBlbnRpdHkgZm9yIG9ic2VydmF0aW9uICRpJC4KICAKKipTcGF0aWFsIGF1dG9jb3JyZWxhdGlvbioqCiAgClRoZSBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBpcyBtb2RlbGxlZCB3aXRoIGFuIGludHJpbnNpYyBjb25kaXRpb25hbCBhdXRvcmVncmVzc2l2ZSAoaUNBUikgcHJvY2VzczoKClxiZWdpbntlcXVhdGlvbn0KXHJob19qIFxzaW0gXG1hdGhjYWx7Tn1vcm1hbChcbXVfaixWX3tccmhvfSAvIG5faikKXGVuZHtlcXVhdGlvbn0KCiRcbXVfaiQ6IG1lYW4gb2YgJFxyaG9fe2onfSQgaW4gdGhlIG5laWdoYm9yaG9vZCBvZiAkaiQuCiAgCiRWX3tccmhvfSQ6IHZhcmlhbmNlIG9mIHRoZSBzcGF0aWFsIHJhbmRvbSBlZmZlY3RzLgogIAokbl9qJDogbnVtYmVyIG9mIG5laWdoYm9ycyBmb3Igc3BhdGlhbCBlbnRpdHkgJGokLgoKYGBge3IgaUNBUl9wcm9jZXNzLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygidHJhaW5pbmcvZmlndXJlcy9pQ0FSLnBuZyIpCmBgYAoKRmlndXJlIGNhcHRpb246ICoqUmVwcmVzZW50YXRpb24gb2YgdGhlIG5laWdoYm9yaG9vZCBmb3IgdGhlIGludHJpbnNpYyBjb25kaXRpb25hbCBhdXRvcmVncmVzc2l2ZSAoaUNBUikgcHJvY2Vzcy4qKiBUYXJnZXQgc3BhdGlhbCBjZWxsICRqJCBoYXMgOCBuZWlnaGJvcnMgaW4gdGhpcyBjYXNlLiBTZXZlcmFsIG9ic2VydmF0aW9ucyAoYmxhY2sgcG9pbnRzLCBlcXVpdmFsZW50IHRvIHBpeGVsIGNlbnRlcnMgaW4gb3VyIGNhc2UpIGNhbiBiZSBsb2NhdGVkIGluIGVhY2ggc3BhdGlhbCBjZWxsLiBEZWZvcmVzdGF0aW9uIHByb2JhYmlsaXR5IGluIG9uZSBzcGF0aWFsIGNlbGwgJGokIGRlcGVuZHMgb24gZGVmb3Jlc3RhdGlvbiBwcm9iYWJpbGl0eSBpbiBuZWlnaGJvcmluZyBjZWxscy4KCiMjIyBTcGF0aWFsIGNlbGxzCgpCZWZvcmUgcnVubmluZyB0aGUgbW9kZWwsIHdlIGFkZCBhIGNvbHVtbiBpbmRpY2F0aW5nIHRoZSBudW1iZXIgb2YgdHJpYWxzIGZvciBlYWNoIG9ic2VydmF0aW9uICgxIGluIG91ciBjYXNlIGFzIHdlIGFyZSBjb25zaWRlcmluZyBhIEJlcm5vdWxsaSBwcm9jZXNzKS4gV2UgdGhlbiByZW1vdmUgYW55IG9ic2VydmF0aW9uIHdpdGggbm9uLWF2YWlsYWJsZSBkYXRhIChOQSkgZnJvbSB0aGUgZGF0YS1zZXQuIFdlIGFsc28gY29tcHV0ZSB0aGUgbnVtYmVyIG9mIG5laWdoYm9ycyAoYG5uZWlnaGApIGFuZCB0aGUgbmVpZ2hib3IgaWRlbnRpZmllcnMgKGBhZGpgKSBmb3IgZWFjaCBzcGF0aWFsIGNlbGwgdXNpbmcgZnVuY3Rpb24gYC5jZWxsbmVpZ2hgIGZyb20gdGhlIGBmb3Jlc3RhdHJpc2tgIG1vZHVsZS4KCmBgYHtyIHNwYXRpYWxfY2VsbHN9CiMgU3BhdGlhbCBjZWxscyBmb3Igc3BhdGlhbC1hdXRvY29ycmVsYXRpb24KbmVpZ2hib3Job29kIDwtIGZhciRjZWxsbmVpZ2hfY3RyeShyYXN0ZXI9ImRhdGEvbW9kZWwvZm9yZGVmb3IyMDEwLnRpZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVjdG9yPSJkYXRhL21hZGEvbWFkYTM4cy5zaHAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNzaXplPTEwTCwgcmFuaz0xTCkKbm5laWdoIDwtIG5laWdoYm9yaG9vZFtbMV1dCmFkaiA8LSBuZWlnaGJvcmhvb2RbWzJdXQpjZWxsX2luIDwtIG5laWdoYm9yaG9vZFtbM11dCm5jZWxsIDwtIG5laWdoYm9yaG9vZFtbNF1dCgojIFVkcGF0ZSBjZWxsIG51bWJlciBpbiBkYXRhc2V0CmNlbGxfcmFuayA8LSB2ZWN0b3IoKQpmb3IgKGkgaW4gMTpucm93KGRhdGFfdHJhaW4pKSB7CiAgY2VsbF9yYW5rW2ldIDwtIHdoaWNoKGNlbGxfaW49PWRhdGFfdHJhaW4kY2VsbFtpXSktMSAjICEgY2VsbHMgc3RhcnQgYXQgemVybwp9CmRhdGFfdHJhaW4kY2VsbCA8LSBjZWxsX3JhbmsKCmBgYAoKIyMjIE1vZGVsIGZvcm11bGEKCkEgbW9kZWwgZm9ybXVsYSBtdXN0IGFsc28gYmUgZGVmaW5lZCB0byBzcGVjaWZ5IHRoZSBleHBsaWNhdGl2ZSB2YXJpYWJsZXMgd2Ugd2FudCB0byBpbmNsdWRlIGluIHRoZSBtb2RlbC4gVGhlIGZvcm11bGEgYWxsb3dzIHNwZWNpZnlpbmcgc29tZSB2YXJpYWJsZSB0cmFuc2Zvcm1hdGlvbnMgKHN1Y2ggYXMgc3RhbmRhcmRpemF0aW9uIGluIG91ciBjYXNlKS4gU2VlIHRoZSBbYHBhdHN5YF0oaHR0cHM6Ly9wYXRzeS5yZWFkdGhlZG9jcy5pby9lbi9sYXRlc3QvKSBtb2R1bGUgZm9yIG1vcmUgaW5mb3JtYXRpb24uIEluIG91ciBtb2RlbCwgd2UgaW5jbHVkZWQgdGhlIGZvbGxvd2luZyB2YXJpYWJsZXM6IGxvY2F0aW9uIGluc2lkZSBhIHByb3RlY3RlZCBhcmVhLCBhbHRpdHVkZSwgZGlzdGFuY2UgdG8gcGFzdCBkZWZvcmVzdGF0aW9uICh3aXRoIGEgZGVncmVlIHR3byBwb2x5bm9taWFsKSwgZGlzdGFuY2UgdG8gZm9yZXN0IGVkZ2UsIGRpc3RhbmNlIHRvIG5lYXJlc3Qgcm9hZCBhbmQgZGlzdGFuY2UgdG8gbmVhcmVzdCB0b3duLiBUaGUgZm9ybXVsYSBtdXN0IGVuZCB3aXRoIHRoZSBuYW1lIG9mIHRoZSB2YXJpYWJsZSBpbmRpY2F0aW5nIHRoZSBzcGF0aWFsIGNlbGwgZm9yIGVhY2ggb2JzZXJ2YXRpb24gKGBjZWxsYCBpbiBvdXIgY2FzZSkuCgpgYGB7ciBmb3JtdWxhfQojIEZvcm11bGEKZGF0YV90cmFpbiR0cmlhbHMgPC0gMSAgIyBTZXQgbnVtYmVyIG9mIHRyaWFscyB0byBvbmUKZm9ybXVsYSA8LSBwYXN0ZTAoIkkoMS1mb3JkZWZvcjIwMTApICsgdHJpYWxzIH4gQyhzYXBtKSArIHNjYWxlKGFsdGl0dWRlKSArIHNjYWxlKHNsb3BlKSArIiwKICAgICAgICAgICAgICAgICAgInNjYWxlKGRpc3RfZGVmb3IpICsgbnAucG93ZXIoc2NhbGUoZGlzdF9kZWZvciksMikgKyAiLAogICAgICAgICAgICAgICAgICAic2NhbGUoZGlzdF9lZGdlKSArICIsCiAgICAgICAgICAgICAgICAgICJzY2FsZShkaXN0X3JvYWQpICsgc2NhbGUoZGlzdF90b3duKSArIGNlbGwiKQoKCmBgYAoKIyMjIEZpdHRpbmcgbW9kZWwgcGFyYW1ldGVycwoKYGBge3IgbW9kZWx9CiMgTW9kZWwKbW9kX2Jpbm9taWFsX2lDQVIgPC0gZmFyJG1vZGVsX2Jpbm9taWFsX2lDQVIoCiAgIyBPYnNlcnZhdGlvbnMKICBzdWl0YWJpbGl0eV9mb3JtdWxhPWZvcm11bGEsIGRhdGE9ZGF0YV90cmFpbiwKICAjIFNwYXRpYWwgc3RydWN0dXJlCiAgbl9uZWlnaGJvcnM9bnBfYXJyYXkobm5laWdoLGR0eXBlPSJpbnQzMiIpLCBuZWlnaGJvcnM9bnBfYXJyYXkoYWRqLGR0eXBlPSJpbnQzMiIpLAogICMgRW52aXJvbm1lbnQKICBldmFsX2Vudj0tMUwsCiAgIyBDaGFpbnMKICBidXJuaW49MTAwMEwsIG1jbWM9MTAwMEwsIHRoaW49MUwsCiAgIyBTdGFydGluZyB2YWx1ZXMKICBiZXRhX3N0YXJ0PS05OSkKCmBgYAoKIyMjIE1vZGVsIHN1bW1hcnkKCk9uY2UgdGhlIG1vZGVsIGhhcyBiZWVuIGZpdHRlZCwgd2UgY2FuIHByaW50IGEgc3VtbWFyeSBvZiB0aGUgbW9kZWwgc2hvd2luZyB0aGUgcGFyYW1ldGVyIGVzdGltYXRlcy4gVGhlIDk1JSBjcmVkaWJsZSBpbnRlcnZhbHMgb2J0YWluZWQgZnJvbSB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiBvZiBlYWNoIHBhcmFtZXRlciwgZXhjZXB0IGRpc3RhbmNlIHRvIG5lYXJlc3QgdG93biAoYGRpc3RfdG93bmApLCBkbyBub3QgaW5jbHVkZSB6ZXJvLCBpbmRpY2F0aW5nIHRoYXQgcGFyYW1ldGVycyBhcmUgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZnJvbSB6ZXJvLiBUaGUgdmFyaWFuY2Ugb2YgdGhlIHNwYXRpYWwgcmFuZG9tIGVmZmVjdHMgKGBWcmhvYCkgaXMgZ2l2ZW4gdG9nZXRoZXIgd2l0aCB0aGUgZGV2aWFuY2UgdmFsdWUsIHdoaWNoIGNhbiBiZSB1c2VkIHRvIGNvbXBhcmUgZGlmZmVyZW50IHN0YXRpc3RpY2FsIG1vZGVscyAobG93ZXIgZGV2aWFuY2UgaXMgYmV0dGVyKS4gTG9va2luZyBhdCB0aGUgcGFyYW1ldGVyIGVzdGltYXRlcywgd2UgY2FuIHNlZSB0aGF0IHRoZSBkZWZvcmVzdGF0aW9uIHByb2JhYmlsaXR5IGlzIG11Y2ggbG93ZXIgaW5zaWRlIHByb3RlY3RlZCBhcmVhcyBhbmQgdGhhdCBkZWZvcmVzdGF0aW9uIHByb2JhYmlsaXR5IGRlY3JlYXNlcyB3aXRoIGFsdGl0dWRlLCBzbG9wZSwgZGlzdGFuY2UgdG8gcGFzdCBkZWZvcmVzdGF0aW9uLCBmb3Jlc3QgZWRnZSwgcm9hZHMgYW5kIHRvd25zLiBQYXJhbWV0ZXIgdmFsdWVzIGFyZSB0aGVuIGNvaGVyZW50IHJlZ2FyZGluZyB0aGUgZGVmb3Jlc3RhdGlvbiBwcm9jZXNzIGFuZCBlYXN5IHRvIGludGVycHJldC4KCmBgYHtyfQpzaW5rKGZpbGU9Im91dHB1dC9zdW1tYXJ5X21vZF9iaW5vbWlhbF9pQ0FSLnR4dCIpCnByaW50KG1vZF9iaW5vbWlhbF9pQ0FSKQpzaW5rKCkKcHJpbnQobW9kX2Jpbm9taWFsX2lDQVIpCmBgYAoKIyMjIFBsb3QgdHJhY2VzIGFuZCBwb3N0ZXJpb3JzCgpUbyBjaGVjayBmb3IgdGhlIGNvbnZlcmdlbmNlIG9mIHRoZSBNYXJrb3YgY2hhaW4gTW9udGUgQ2FybG8gKE1DTUMpLCB3ZSBjYW4gcGxvdCB0aGUgdHJhY2VzIGFuZCB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbnMgb2YgdGhlIGVzdGltYXRlZCBwYXJhbWV0ZXJzIHVzaW5nIG1ldGhvZCBgLnBsb3QoKWAgYXNzb2NpYXRlZCB0byB0aGUgYGhTRE1fYmlub21pYWxfaUNBUmAgY2xhc3MgZGVmaW5lZCBpbiB0aGUgYGRlZm9yZXN0cHJvYmAgbW9kdWxlLiBUaGlzIG1ldGhvZCByZXR1cm5zIHRoZSBmaWd1cmVzIHNob3dpbmcgdGhlIHRyYWNlcyBhbmQgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbnMuCgpgYGB7ciBwbG90X3dpdGhfcHksIGVjaG89RkFMU0V9CnRyYWNlc19maWcgPC0gbW9kX2Jpbm9taWFsX2lDQVIkcGxvdChvdXRwdXRfZmlsZT0ib3V0cHV0L21jbWMucGRmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RzX3Blcl9wYWdlPTNMLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlnc2l6ZT1jKDksNiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGk9MTAwKQpgYGAKCmBgYHtyIHBsb3Rfd2l0aF9yfQpyZXF1aXJlKGNvZGEpCm1jbWMgPC0gYXMubWNtYyhtb2RfYmlub21pYWxfaUNBUiRtY21jKQpwbG90KG1jbWNbLGMoMTozLDEwLDExKV0pCmBgYAoKIyMgRm9yZWNhc3RpbmcgZGVmb3Jlc3RhdGlvbgoKIyMjIFJlc2FtcGxpbmcgdGhlIHNwYXRpYWwgcmFuZG9tIGVmZmVjdHMKCldlIHVzZSB0aGUgbW9kZWwgdG8gcHJlZGljdCB0aGUgc3BhdGlhbCBwcm9iYWJpbGl0eSBvZiBkZWZvcmVzdGF0aW9uIGF0IHRoZSBuYXRpb25hbCBzY2FsZSBmb3IgTWFkYWdhc2Nhci4gQmVmb3JlLCBkb2luZyBzbywgd2Ugc21vb3RoIHRoZSBzcGF0aWFsIHJhbmRvbSBlZmZlY3RzIHdoaWNoIGhhdmUgYmVlbiBlc3RpbWF0ZWQgYXQgYSBjb2Fyc2UgcmVzb2x1dGlvbiAoMTBrbSBpbiBvdXIgZXhhbXBsZSkuIFRvIGRvIHNvLCB3ZSB1c2UgdGhlIGZ1bmN0aW9uIGAucmVzYW1wbGVfcmhvKClgIGZyb20gdGhlIGBkZWZvcmVzdHByb2JgIG1vZHVsZSB0byByZXNhbXBsZSB0aGUgcmVzdWx0cyBhdCBhIGZpbmVyIHJlc29sdXRpb24gdXNpbmcgYSBiaWxpbmVhciBpbnRlcnBvbGF0aW9uLiBUaGUgZnVuY3Rpb24gd3JpdGVzIGEgcmFzdGVyIGZpbGUgdG8gdGhlIGRpc2sgd2l0aCBhIHJlc29sdXRpb24gb2YgdGhlIHJhc3RlciBzcGVjaWZpZWQgaW4gdGhlIGFyZ3VtZW50IGBpbnB1dF9yYXN0ZXJgIG9mIHRoZSBmdW5jdGlvbiAoMWttIGluIG91ciBjYXNlKS4gVGhlIGZ1bmN0aW9uIGAucmVzYW1wbGVfcmhvKClgIHJldHVybnMgYSBmaWd1cmUgb2YgdGhlIHNwYXRpYWwgcmFuZG9tIGVmZmVjdHMgdGhhdCBjYW4gYmUgcGxvdHRlZC4KCmBgYHtyIHJobywgZmlnLmhlaWdodD03LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIEdldCB0aGUgc3BhdGlhbCByYW5kb20gZWZmZWN0cwpyaG8gPC0gcmVwKC05OTk5LG5jZWxsKSAgIyAtOTk5OSB3aWxsIGJlIGNvbnNpZGVyZWQgYXMgbm9kYXRhCnJob1tjZWxsX2luKzFdIDwtIG1vZF9iaW5vbWlhbF9pQ0FSJHJobwoKIyBSZXNhbXBsZSB0aGVtCmZpZyA8LSBmYXIkcmVzYW1wbGVfcmhvKHJobz1yX3RvX3B5KHJobyksIGlucHV0X3Jhc3Rlcj0iZGF0YS9tb2RlbC9mb3JkZWZvcjIwMTAudGlmIiwKICAgICAgICAgICAgICAgICBvdXRwdXRfZmlsZT0ib3V0cHV0L3Joby50aWYiLAogICAgICAgICAgICAgICAgIGNzaXplX29yaWc9MTBMLCBjc2l6ZV9uZXc9MUwpCgojIFBsb3QgcmFuZG9tIGVmZmVjdHMKZmlnIDwtIGZhciRwbG90JHJobygib3V0cHV0L3Job19vcmlnLnRpZiIsb3V0cHV0X2ZpbGU9Im91dHB1dC9yaG9fb3JpZy5wbmciKQpmaWcgPC0gZmFyJHBsb3QkcmhvKCJvdXRwdXQvcmhvLnRpZiIsb3V0cHV0X2ZpbGU9Im91dHB1dC9yaG8ucG5nIikKCiMgUGxvdCB3aXRoIFIKbWFkYSA8LSByZ2RhbDo6cmVhZE9HUihkc249ImRhdGEvbWFkYSIsbGF5ZXI9Im1hZGEzOHMiLCB2ZXJib3NlPUZBTFNFKQpyLnJob19vcmlnIDwtIHJhc3Rlcigib3V0cHV0L3Job19vcmlnLnRpZiIpCnIucmhvIDwtIHJhc3Rlcigib3V0cHV0L3Joby50aWYiKQpyaG9fcGxvdChyLnJob19vcmlnLCBtYWRhLCBvdXRwdXRfZmlsZT0ib3V0cHV0L3Job19vcmlnX2dncGxvdC5wbmciLAogICAgICAgICBxdWFudGlsZXNfbGVnZW5kPWMoMC4wMjUsMC45NzUpLHdpZHRoPTQuNSwgaGVpZ2h0PTgpCnJob19wbG90KHIucmhvLCBtYWRhLCBvdXRwdXRfZmlsZT0ib3V0cHV0L3Job19nZ3Bsb3QucG5nIiwKICAgICAgICAgcXVhbnRpbGVzX2xlZ2VuZD1jKDAuMDI1LDAuOTc1KSx3aWR0aD00LjUsIGhlaWdodD04KQoKYGBgCgojIyMgQ29tcHV0aW5nIHNwYXRpYWwgcHJvYmFiaWxpdHkgb2YgZGVmb3Jlc3RhdGlvbgoKVGhlIGAucHJlZGljdF9yYXN0ZXJfYmlub21pYWxfaUNBUigpYCBmdW5jdGlvbiBvZiB0aGUgYGZvcmVzdGF0cmlza2AgbW9kdWxlIGNhbiBiZSB1c2VkIHRvIHByZWRpY3QgdGhlIHNwYXRpYWwgcHJvYmFiaWxpdHkgb2YgZGVmb3Jlc3RhdGlvbiBmcm9tIGFuICoqaFNETV9iaW5vbWlhbF9pQ0FSKiogbW9kZWwgKGkuZS4gYW4gb2JqZWN0IG9mIGNsYXNzIGBoU0RNX2Jpbm9taWFsX2lDQVJgKS4gVGhlIGZ1bmN0aW9uIHdyaXRlcyBhIHJhc3RlciBvZiBwcmVkaWN0aW9ucyB0byB0aGUgZGlzay4gVGhlIHByZWRpY3Rpb24gaXMgZG9uZSBieSBibG9jayB0byBhdm9pZCBtZW1vcnkgcHJvYmxlbXMgZm9yIGJpZyBkYXRhc2V0cy4gRnVuY3Rpb25zIHdpbGwgcmV0dXJuIE5BIGZvciBwaXhlbHMgd2l0aCBubyBmb3Jlc3Qgb3IgZm9yIHBpeGVscyB3aXRoIG1pc3NpbmcgZW52aXJvbm1lbnRhbCB2YXJpYWJsZXMuCgpgYGB7ciBjb21wdXRlX3ByZWRpY3Rpb25zfQpmYXIkcHJlZGljdF9yYXN0ZXJfYmlub21pYWxfaUNBUihtb2RfYmlub21pYWxfaUNBUiwgdmFyX2Rpcj0iZGF0YS9tb2RlbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlucHV0X2NlbGxfcmFzdGVyPSJvdXRwdXQvcmhvLnRpZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlucHV0X2ZvcmVzdF9yYXN0ZXI9ImRhdGEvbW9kZWwvZm9yZGVmb3IyMDEwLnRpZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dF9maWxlPSJvdXRwdXQvcHJlZF9iaW5vbWlhbF9pQ0FSLnRpZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsa19yb3dzPTEyOEwpCgpgYGAKClRoZSByYXN0ZXIgb2YgcHJlZGljdGlvbnMgY2FuIHRoZW4gYmUgcGxvdHRlZC4KCmBgYHtyIHBsb3RfcHJlZGljdGlvbnN9CmZpZyA8LSBmYXIkcGxvdCRwcm9iKCJvdXRwdXQvcHJlZF9iaW5vbWlhbF9pQ0FSLnRpZiIsCiAgICAgICAgICAgICAgICAgICAgIG91dHB1dF9maWxlPSJvdXRwdXQvcHJlZF9iaW5vbWlhbF9pQ0FSLnBuZyIsCiAgICAgICAgICAgICAgICAgICAgIGZpZ3NpemU9Yyg0LDQpKQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygib3V0cHV0L3ByZWRfYmlub21pYWxfaUNBUi5wbmciKQpgYGAKCiMjIyBQcmVkaWN0aW5nIGZ1dHVyZSBmb3Jlc3QgY292ZXIKCkdpdmVuIHRoZSBzcGF0aWFsIHByb2JhYmlsaXR5IG9mIGRlZm9yZXN0YXRpb24gYW5kIHRoZSBudW1iZXIgb2YgaGVjdGFyZXMgdGhhdCBzaG91bGQgYmUgZGVmb3Jlc3RlZCwgd2UgY2FuIHByZWRpY3QgdGhlIGZ1dHVyZSBmb3Jlc3QgY292ZXIgdXNpbmcgZnVuY3Rpb24gYC5kZWZvcmVzdCgpYCBmcm9tIHRoZSBgZm9yZXN0YXRyaXNrYCBwYWNrYWdlLiBUaGUgbnVtYmVyIG9mIGhlY3RhcmVzIGFyZSBjb252ZXJ0ZWQgaW50byBudW1iZXIgb2YgcGl4ZWxzIHRvIGJlIGRlZm9yZXN0ZWQuIFBpeGVscyB3aXRoIHRoZSBoaWdoZXN0IHByb2JhYmlsaXR5IG9mIGRlZm9yZXN0YXRpb24gYXJlIGRlZm9yZXN0ZWQgZmlyc3QuIFRoZSBmdW5jdGlvbiBjb21wdXRlcyBhIHByb2JhYmlsaXR5IHRocmVzaG9sZCBhYm92ZSB3aGljaCBwaXhlbHMgYXJlIGRlZm9yZXN0ZWQuCgpJbiBvdXIgZXhhbXBsZSwgd2UgY29uc2lkZXIgYW4gYW5udWFsIGRlZm9yZXN0YXRpb24gb2Ygcm91Z2hseSAxMDAsMDAwIGhhIGZvciBNYWRhZ2FzY2FyLiBDb25zaWRlcmluZyB0aGUgcGVyaW9kIDIwMTAtMjA1MCwgdGhpcyB3b3VsZCBjb3JyZXNwb25kIHRvIDQgTWhhIG9mIGRlZm9yZXN0YXRpb24uIFRoaXMgbnVtYmVyIGNhbiBvZiBjb3Vyc2UgYmUgbW9yZSBwcmVjaXNlIGFuZCByZWZpbmVkIGNvbnNpZGVyaW5nIHZhcmlvdXMgZGVmb3Jlc3RhdGlvbiBzY2VuYXJpb3MgKGRlbW9ncmFwaGljIGdyb3d0aCwgZWNvbm9taWMgZGV2ZWxvcG1lbnQsIGV0Yy4pLgoKYGBge3IgY29tcHV0ZV9mdXR1cmVfZm9yZXN0X2NvdmVyfQpkZWZvcmVzdCA8LSBmYXIkZGVmb3Jlc3QoaW5wdXRfcmFzdGVyPSJvdXRwdXQvcHJlZF9iaW5vbWlhbF9pQ0FSLnRpZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICBoZWN0YXJlcz00MDAwMDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgb3V0cHV0X2ZpbGU9Im91dHB1dC9mb3Jlc3RfY292ZXJfMjA1MC50aWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgYmxrX3Jvd3M9MTI4TCkKYGBgCgpXZSBjYW4gcGxvdCB0aGUgcHJlZGljdGVkIGZ1dHVyZSBmb3Jlc3QgY292ZXIgaW4gMjA1MCB3aXRoIFtsZWFmbGV0XShodHRwOi8vcnN0dWRpby5naXRodWIuaW8vbGVhZmxldC8pLiBXZSBmaXJzdCByZXByb2plY3QgdGhlIHJhc3RlciB0byBXR1MgODQgLyBXb3JsZCBNZXJjYXRvciBwcm9qZWN0aW9uIChbRVBTRzozODU3XShodHRwOi8vc3BhdGlhbHJlZmVyZW5jZS5vcmcvcmVmL3NyLW9yZy83NDgzLykpIGFuZCB3ZSByZXNhbXBsZSB0aGUgbWFwIGF0IDI1MCBtIHVzaW5nIGZ1bmN0aW9uIGBnZGFsd2FycGAgZnJvbSBgR0RBTGAgdG8gb2J0YWluIGEgbG93ZXIgcmVzb2x1dGlvbiByYXN0ZXIgdGhhdCBjYW4gYmUgdHJhbnNmb3JtZWQgaW50byBhIGxvd2VyIHNpemUgaW1hZ2UgdGhhdCBjYW4gYmUgcGxvdHRlZCB3aXRoIGxlYWZsZXQuCgpgYGB7YmFzaH0KZ2RhbHdhcnAgLW92ZXJ3cml0ZSAtc19zcnMgRVBTRzozMjczOCAtdF9zcnMgRVBTRzozODU3IC10ciAyNTAgMjUwIFwKICAgICAgICAgLW90IEJ5dGUgLXIgbmVhciAtY28gIkNPTVBSRVNTPUxaVyIgLWNvICJQUkVESUNUT1I9MiIgLWNvICJCSUdUSUZGPVlFUyIgXAogICAgICAgICBvdXRwdXQvZm9yZXN0X2NvdmVyXzIwNTAudGlmIG91dHB1dC9mb3Jlc3RfY292ZXJfMjA1MF9lcHNnMzg1N19vdjMyLnRpZgpgYGAKCldlIGFsc28gc2V0IHRoZSBjb2xvciBvZiB0aGUgbWFwIGFuZCB0aGUgZXh0ZW50IG9mIHRoZSB2aWV3IGZvciBsZWFmbGV0LgoKYGBge3J9CiMgQ29sb3JzIGFuZCBleHRlbnQgdmlldyBmb3IgbGVhZmxldApyIDwtIHJhc3Rlcigib3V0cHV0L2ZvcmVzdF9jb3Zlcl8yMDUwX2Vwc2czODU3X292MzIudGlmIikKcGFsIDwtIGNvbG9yRmFjdG9yKGMoInJlZCIsICJkYXJrZ3JlZW4iKSwgYygwLDEpLCBuYS5jb2xvciA9ICJ0cmFuc3BhcmVudCIpCnIxIDwtIHJhc3Rlcihucm93cz0xLG5jb2xzPTEsZXh0PWV4dGVudCgzNDAwMDAsNDEyMDAwLDc0MjAwMDAsNzUwMDAwMCksY3JzPUNSUygiK2luaXQ9ZXBzZzozMjczOCIpKQpyMiA8LSBwcm9qZWN0RXh0ZW50KHIxLCBjcnM9Q1JTKCIraW5pdD1lcHNnOjQzMjYiKSkKZXh0MiA8LSBleHRlbnQocjIpCmBgYAoKVGhlIHJlZCBhcmVhcyByZXByZXNlbnQgdGhlIGRlZm9yZXN0YXRpb24gb24gdGhlIHBlcmlvZCAyMDEwLTIwNTAuIFRoZSBncmVlbiBhcmVhcyByZXByZXNlbnQgdGhlIHJlbWFpbmluZyBmb3Jlc3QgaW4gMjA1MC4gTW9zdCBvZiB0aGUgcmVtYWluaW5nIGZvcmVzdCBpbiAyMDUwIGFyZSBpbnNpZGUgdGhlIHByb3RlY3RlZCBhcmVhcyBvciBsb2NhdGVkIGluIHJlbW90ZSBhcmVhcywgYXQgaGlnaCBhbHRpdHVkZXMgYW5kIGZhciBmcm9tIHJvYWRzIGFuZCBiaWcgY2l0aWVzIChmb3IgZXhhbXBsZSBpbiB0aGUgVHNhcmF0YW5hbmEgbW91bnRhaW4gcmVnaW9uIGFuZCBhcm91bmQgdGhlIE1hc29hbGEgcGVuaW5zdWxhLCBub3J0aC1lYXN0IE1hZGFnYXNjYXIpLgoKYGBge3IgZmNjX2xlYWZsZXR9CiMgTGVhZmxldCBtYXAKbSA8LSBsZWFmbGV0KCkgJT4lIGFkZFRpbGVzKCkgJT4lCiAgYWRkUmFzdGVySW1hZ2UociwgY29sb3JzPXBhbCwgb3BhY2l0eT0wLjgsIHByb2plY3Q9RkFMU0UpICU+JQogIGZpdEJvdW5kcyhleHQyQHhtaW4sZXh0MkB5bWluLGV4dDJAeG1heCxleHQyQHltYXgpCm0KYGBgCgojIyBSZWZlcmVuY2Vz